2017-10-07离线赛

大体状况

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 long

void 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 0x3f3f3f3f3f3f3f3f

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 M 200004
int 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写得太麻烦了,
数据卡一下可能还会因为常数大的关系死掉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值