2017-10-07离线赛

来源:互联网 发布:沈阳 苹果直营店 知乎 编辑:程序博客网 时间:2024/05/18 16:14

大体状况

240/300

T1 simple

题目大意

[1,q]内满足有非负整数解xn+ym=c的数的个数。

分析

P60

这个不定方程虽然是exgcd的形式,
然而完全可以写DP,然后直接计算。
实际上就是个暴力。

P100

因为不会写,
又看到n105
就考虑在modn下讨论,
然后调了一会凑出了答案,对拍无误。

代码

瞎写。

#include<bits/stdc++.h>using namespace std;#define Komachi is retarded#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)#define chkmin(a,b) a=min(a,b)#define chkmax(a,b) a=max(a,b)#define LL long longvoid Rd(int &res){    char c;res=0;    while((c=getchar())<48);    do res=(res<<3)+(res<<1)+(c^48);    while((c=getchar())>47);}void Rd(LL &res){    char c;res=0;    while((c=getchar())<48);    do res=(res<<3)+(res<<1)+(c^48);    while((c=getchar())>47);}int T,n[14],m[14];LL q[14],mx;struct P60{    static const int M=100004;    int DP[M];    void Solve(){        REP(t,0,T){            REP(i,0,M)if(i%n[t])DP[i]=0;            else DP[i]=1;            REP(i,0,M-m[t]-1)if(DP[i])                DP[i+m[t]]=1;            REP(i,0,M)DP[i]=!DP[i];            REP(i,1,M)DP[i]+=DP[i-1];            printf("%d\n",DP[q[t]]);        }    }}P60;struct P100{    static const int M=100004;    LL DP[M];    int Q[M];    void Solve(){        REP(t,0,T){            memset(DP,63,sizeof(DP));            int A=DP[0]=0;            LL val=0;            while(1){                (A+=m[t])%=n[t];                val+=m[t];                if(val>q[t])break;                if(val<DP[A])DP[A]=val;                else break;            }            LL Ans=q[t];            REP(i,0,M)if(DP[i]!=0x3f3f3f3f3f3f3f3f){                Ans-=(q[t]-DP[i])/n[t]+1;            }            Ans++;            printf("%lld\n",Ans);        }    }}P100;int main(){    freopen("simple.in","r",stdin);    freopen("simple.out","w",stdout);    Rd(T);    REP(t,0,T)Rd(n[t]),Rd(m[t]),Rd(q[t]),chkmax(mx,q[t]);    if(mx<=100000)P60.Solve();    else P100.Solve();    return 0;}

T2 walk

题目大意

求树上每一种长度的路径所得到的边权最大Gcd。

P30

暴力枚举一个起点。
暴力搜索同时计算Gcd。
暴力更新答案。

P100

然后发现答案应该是单调递减的。
边权的范围较小,所以可以枚举答案。
然后用边权为答案的倍数的这些边建森林,
求每棵树的直径,取最大值用于更新即可。

代码

比赛时没有枚举答案倍数,弄了一个枚举边权因子。
为此写了线性筛素数与其副产品线性拆质因子。
以及DFS枚举因子和时间戳类链表。
速度十分慢,还好是卡过去了。

#include<bits/stdc++.h>using namespace std;#define Komachi is retarded#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)#define LL long long#define chkmin(a,b) a=min(a,b)#define chkmax(a,b) a=max(a,b)#define M 400004#define pb push_back#define SZ(a) ((int)(a).size())void Rd(int &res){    char c;res=0;    while((c=getchar())<48);    do res=(res<<3)+(res<<1)+(c^48);    while((c=getchar())>47);}#define LREP(i,A) for(int i=Head[A];i;i=Next[i])int n;struct P30{    int Next[M<<1],V[M<<1],W[M<<1],Head[M],tot;    void Add_Edge(int u,int v,int w){        Next[++tot]=Head[u],V[Head[u]=tot]=v,W[tot]=w;    }    int Gcd(int a,int b){        return b?Gcd(b,a%b):a;    }    int Ans[1004];    void DFS(int A,int f,int d,int num){        int B;        if(d)chkmax(Ans[d],num);        LREP(i,A)if((B=V[i])!=f)            DFS(B,A,d+1,Gcd(W[i],num));    }    void Solve(){        REP(i,1,n){            int u,v,w;            Rd(u),Rd(v),Rd(w);            Add_Edge(u,v,w);            Add_Edge(v,u,w);        }        memset(Ans,0,sizeof(Ans));        REP(i,1,n+1)DFS(i,0,0,0);        REP(i,1,n+1)printf("%d\n",Ans[i]);    }}P30;struct P100{    static const int Mx=1000004;    int Pri[Mx>>1],pt;    int B[Mx];    int U[M],V[M],Tmp[20];    bool Vis[20];    int Ans[M],space;    vector<int>Use[Mx];    void Updata(int x,int cnt,int num,int id){        if(x==cnt)return;        if(x && !Vis[x-1] && Tmp[x]==Tmp[x-1]){            Updata(x+1,cnt,num,id);            return;        }        Updata(x+1,cnt,num,id);        Vis[x]=1;        num*=Tmp[x];        Use[num].pb(id);space++;        Updata(x+1,cnt,num,id);        Vis[x]=0;    }    void Init(){        REP(i,2,Mx){            if(!B[i])Pri[pt++]=B[i]=i;            REP(j,0,pt){                LL pos=1ll*i*Pri[j];                if(pos>=Mx)break;                B[pos]=Pri[j];                if(!(i%Pri[j]))break;            }        }    }    int Next[M<<1],Go[M<<1],Tim[M],Head[M],tot,Time;    void Add_Edge(int u,int v){        if(Tim[u]!=Time)Tim[u]=Time,Head[u]=0;        Next[++tot]=Head[u],Go[Head[u]=tot]=v;    }    void TreeClear(){        Time++;        tot=0;    }    bool Mark[M];    int Dis[M];    int DFS(int A,int f,int &To){        Mark[A]=1;        Dis[A]=Dis[f]+1;        if(Dis[A]>Dis[To])To=A;        int B;        LREP(i,A)if((B=Go[i])!=f)            DFS(B,A,To);    }    void Check(int PAns){        if(!SZ(Use[PAns]))return;        TreeClear();        int sz=0;        REP(i,0,SZ(Use[PAns])){            int j=Use[PAns][i];            Mark[U[j]]=Mark[V[j]]=0;            Add_Edge(U[j],V[j]);            Add_Edge(V[j],U[j]);        }        int Res=1;        REP(i,0,SZ(Use[PAns])){            int j=Use[PAns][i];            if(!Mark[U[j]]){                int LT,RT;LT=RT=0;                Dis[0]=-1;                DFS(U[j],0,LT);                DFS(LT,0,RT);                chkmax(Res,Dis[RT]);            }        }//      cerr<<Res<<","<<PAns<<endl;        chkmax(Ans[Res],PAns);    }    void Solve(){        Init();        memset(Ans,0,sizeof(Ans));        REP(i,1,n){            int cnt=0,w;            Rd(U[i]),Rd(V[i]),Rd(w);            chkmax(Ans[1],w);            while(B[w])Tmp[cnt++]=B[w],w/=B[w];            Updata(0,cnt,1,i);            Add_Edge(U[i],V[i]);            Add_Edge(V[i],U[i]);        }        int LT,RT;LT=RT=0;        Dis[0]=-1;        DFS(1,0,LT);        DFS(LT,0,RT);        chkmax(Ans[Dis[RT]],1);        REP(i,2,Ans[1]+1)            Check(i);        DREP(i,n,1)            chkmax(Ans[i],Ans[i+1]);        REP(i,1,n+1)            printf("%d\n",Ans[i]);    }}P100;int main(){    freopen("walk.in","r",stdin);    freopen("walk.out","w",stdout);    Rd(n);    if(n<=1000)P30.Solve();    else P100.Solve();//  cerr<<(sizeof(P30)+sizeof(P100)+P100.space*4.0)/1024/1024<<endl;    return 0;}

T3 travel

题目大意

在数轴上瞎跳,求从s开始刚好向左跳L次并刚好经过每个点一次的最小距离以及这个跳跃方案。

P10

O(n!)暴力搜索所有方案。

P???

比赛时间还剩30分钟
思考一下,发现很可能不走回头路,
于是就分为两种情况直接贪心了。
然而这个贪心是错的。
然后水了40分。

P100

然后确实是贪心,但是不是那么蠢。
还是会走回头路的,此时可以用掉一个L。
代价就是这一条线段要走3遍。
枚举结束节点e,使s始终在e左侧。
然后可以得到一种方案,
在s左侧都走两遍,在e右侧也走两遍,用掉n-e+s-1个L,
此时在中间取L个最小线段走三次即可。
然后反向搜索,
由于取的线段一直在变少,可以直接用一个队列维护。
找到答案后随便求出一个可行的方案即可。

代码

感觉几乎是抄题解。

#include<bits/stdc++.h>using namespace std;#define Komachi is retarded#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)#define chkmin(a,b) a=min(a,b)#define chkmax(a,b) a=max(a,b)#define LL long long#define INF 0x3f3f3f3f3f3f3f3fvoid Rd(int &res){    char c;res=0;    while((c=getchar())<48);    do res=(res<<3)+(res<<1)+(c^48);    while((c=getchar())>47);}#define M 200004int n,l,s,X[M],Y[M];int Ans1[M],Ans2[M];LL ans1,ans2;struct Node{    int v,x;    bool operator <(const Node &P)const{        return v<P.v;    }}D[M];int Pos[M];bool Mark[M];void Solve(int l,int s,int *Ans,int *X,LL &ans){    int len=0,tot=0;    if(l<=s-1){        ans=0LL+X[n]-X[1]+X[s]-X[1];        DREP(i,l,0)Ans[len++]=i;        REP(i,l+1,n+1)if(i!=s)Ans[len++]=i;        return;    }    l-=s-1;    if(l==n-s-1){        ans=0LL+X[n]-X[1]+X[s]-X[1]+X[n]-X[s+1];        REP(i,1,s)Ans[len++]=i;        DREP(i,n,s)Ans[len++]=i;        return;    }    LL Sum=0,Mn,Val;    REP(i,s+2,n) D[++tot]=(Node){X[i]-X[i-1],i};    sort(D+1,D+tot+1);    REP(i,1,tot+1)Pos[D[i].x]=i;    REP(i,1,l+1)Sum+=D[i].v;    Mn=Sum<<1;    int mn=l,p=l,e=n;    DREP(i,n-1,n-l-1){        if(Pos[i]<=p)Sum-=D[Pos[i]].v;        else Sum-=D[p--].v;        while(p && D[p].x>=i)p--;        if((Val=((Sum<<1)+X[n]-X[i]))<Mn)            Mn=Val,e=i,mn=p;    }    DREP(i,s-1,0)Ans[len++]=i;    REP(i,s+2,e)Mark[i]=Pos[i]<=mn;    for(int i=s+1;i<e;i++){        int tmp=i+1;        if(!Mark[tmp])Ans[len++]=i;        else {            while(Mark[tmp])tmp++;            DREP(j,tmp-1,i-1)Ans[len++]=j;            i=tmp-1;        }    }    DREP(i,n,e-1)Ans[len++]=i;    ans=0LL+X[n]-X[1]+X[s]-X[1]+Mn;}int main(){    Rd(n),Rd(l),Rd(s);    REP(i,1,n+1)Rd(X[i]),Y[n-i+1]=-X[i];    if((l==0 && s!=1) || (l==n-1 && s!=n) || l>n-1){        puts("-1");        return 0;    }    Solve(l,s,Ans1,X,ans1);    Solve(n-l-1,n-s+1,Ans2,Y,ans2);    if(ans1<=ans2){        printf("%lld\n",ans1);        REP(i,0,n-1)printf("%d%c",Ans1[i]," \n"[i==n-2]);    }    else{        printf("%lld\n",ans2);        REP(i,0,n-1)printf("%d%c",n-Ans2[i]+1," \n"[i==n-2]);    }    return 0;}

总结

其实好像是Rank1来着,
?运气比较好,大概吧。
注意一下实现了,T2写得太麻烦了,
数据卡一下可能还会因为常数大的关系死掉。

原创粉丝点击