• 2020 CCPC 长春题解


    题目链接:https://codeforces.com/gym/102832

    题解:https://zhuanlan.zhihu.com/p/279287505

    A. Krypton

    分析

    除奖励外,其它的倍率均为 (10),因此只要求出奖励的最大值即可,直接 (0/1) 背包。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N=2010;
    int dp[9][N];
    int a[8]={0,8,18,28,58,128,198,388};
    int cost[8]={0,1,6,28,88,198,328,648};
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=7;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(j>=cost[i])
                    dp[i][j]=max(dp[i-1][j-cost[i]]+a[i],dp[i-1][j]);
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        printf("%d
    ",n*10+dp[7][n]);
        return 0;
    }
    
    

    E. Defense of Valor League

    分析

    对抗博弈。

    代码

    H. Combination Lock

    分析

    二分图博弈模板题,每次变换必然会导致当前数字的数位之和的奇偶性改变,按照奇偶性将点分为两类。其中源点连偶数点,汇点连奇数点,偶数点连奇数点,同时避免不能出现的数。先不把起始点加入,跑一遍,再把起始点加入,跑一遍,通过判断第二遍的匹配数是否为 (0) 来判断胜者。

    代码

    #include <bits/stdc++.h>
    #define pb push_back
    using namespace std;
    //关键在于判断起始状态是否是最大匹配的必须点
    //先求一遍最大匹配,然后把起点删除再求一遍最大匹配,比较两次的结果
    const int N=1e5+100;
    const int inf=0x3f3f3f3f;
    int vis[N];
    int fac[6];
    struct node
    {
        int to,val,rev;
    };
    vector<node>pic[N];
    queue<int>que;
    int layer[N],iter[N],maxn,st,n,m,start,eds;
    void read(int &x)
    {
        x=0;
        int f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<3)+(x<<1)+ch-'0';
            ch=getchar();
        }
        x*=f;
    }
    int sum(int x)
    {
        int res=0;
        while(x)
        {
            res+=(x%10);
            x/=10;
        }
        return res;
    }
    void addedge(int u,int v,int w)
    {
        pic[u].pb(node{v,w,pic[v].size()});
        pic[v].pb(node{u,0,pic[u].size()-1});
    }
    void init(int flag)
    {
        for(int i=0;i<=eds;i++)
            pic[i].clear();
        for(int i=0;i<=maxn;i++)
        {
            if(vis[i]==flag) continue;
            if(sum(i)&1)
            {
                if(i!=st) addedge(i,eds,1);
            }
            else
            {
                if(i!=st) addedge(start,i,1);
                for(int j=1;j<=m;j++)
                {//偶数向奇数连边,偶数连源点,奇数连汇点
                    int y=i,x=i;
                    y/=fac[j-1];
                    y%=10;//写错了,debug好久...
                    x-=y*fac[j-1];
                    int t=(y+1)%10;
                    int d=x+fac[j-1]*t;
                    addedge(i,d,1);
                    t=(y-1+10)%10;
                    d=x+fac[j-1]*t;
                    addedge(i,d,1);
                }
            }
        }
    }
    bool bfs()
    {
        while(!que.empty())
            que.pop();
        for(int i=0;i<=eds;i++)
            layer[i]=-1;
        layer[start]=0;
        que.push(start);
        while(!que.empty())
        {
            int now=que.front();
            que.pop();
            for(auto tmp:pic[now])
            {
                if(layer[tmp.to]<0&&tmp.val>0)
                {
                    layer[tmp.to]=layer[now]+1;
                    que.push(tmp.to);
                    if(tmp.to==eds)
                        return true;
                }
            }
        }
        return false;
    }
    int dfs(int u,int w)
    {
        if(u==eds||w==0)
            return w;
        for(int &i=iter[u];i<pic[u].size();i++)
        {
            node &tmp=pic[u][i];
            if(layer[tmp.to]>layer[u]&&tmp.val>0)
            {
                int d=dfs(tmp.to,min(w,tmp.val));
                if(d>0)
                {
                    tmp.val-=d;
                    pic[tmp.to][tmp.rev].val+=d;
                    return d;
                }
            }
        }
        return 0;
    }
    int dinic()
    {
        int max_flow=0;
        while(bfs())
        {
            for(int i=0;i<=eds;i++)
                iter[i]=0;
            int d=0;
            while((d=dfs(start,inf))>0)
                max_flow+=d;
        }
        return max_flow;
    }
    int main()
    {
        int T;
        read(T);
        fac[0]=1;
        for(int i=1;i<=5;i++)
            fac[i]=fac[i-1]*10;
        while(T--)
        {
            read(m),read(n),read(st);
            maxn=1;//点数
            for(int i=1;i<=m;i++) maxn*=10;
            maxn--;
            start=fac[m];
            eds=start+1;
            for(int i=1;i<=n;i++)
            {
                int d;
                read(d);
                vis[d]=T+1;
            }
            init(T+1);
            int ans=dinic();//先不把起始点加入,跑一遍dinic
            if(sum(st)&1) addedge(st,eds,1);
            else addedge(start,st,1);
            ans=dinic();//再把点加入,看还能不能流
            if(ans==0) printf("Bob
    ");
            else printf("Alice
    ");
        }
        return 0;
    }
    

    K. Ragdoll

    分析

    对于一个数 (x) ,其和另一个数可能的 (gcd) 的个数为其约数的个数,而且 (gcd) 必然为其约数中的一个。假设 (x) 的一个约数为 (g) ,那么有 (gcd(g,x)=g=x igoplus y),所以 (y=xigoplus g),接下来只要再验证一下 (gcd(x,y)=g) 即可求出与 (x) 满足条件的每个 (y) 的值。这个过程可以借助埃式筛在 (O(nlog^2n)) 的时间内完成。

    接下来用 ( ext{unorder_map}) 来维护每棵树中点权的出现次数,每棵树用并查集维护一个树根。在两棵树合并的时候,将小的树向大的树合并,并且修改答案。修改点权时,先将原来的点权的点对减掉,再加上新的点权的贡献。启发式合并,复杂度:(O(nlog n))

    然后,就是二维 ( ext{unorder_map}) 的用法。

    代码

    #include <bits/stdc++.h>
    #define pb push_back
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    int a[N*3],fa[N*3];
    unordered_map<int,int>mp[N*3];
    vector<int>num[N*2];
    ll ans;
    int gcd(int x,int y)
    {
        return y?gcd(y,x%y):x;
    }
    void init()
    {
        int maxn=2e5;//埃式筛预处理出每个数的因数,并判断是否有满足条件的数对
        for(int i=1;i<=maxn;i++)
        {
            for(int j=i+i;j<=maxn;j+=i)
            {
                int t=(i^j);
                if(t>0&&t<=maxn&&gcd(t,j)==i)
                    num[j].pb(t);
            }
        }
    }
    int Find(int x)//并查集维护是否在同一颗树上
    {
        if(x!=fa[x])
            return fa[x]=Find(fa[x]);
        else return x;
    }
    void join(int x,int y)
    {
        int fx=Find(x);
        int fy=Find(y);
        if(fx==fy) return;
        if(mp[fx].size()>mp[fy].size())//默认x为小树,y为大树
            swap(fx,fy);
        for(auto i:mp[fx])//枚举mp[fx]出现的点权
        {
            for(auto j:num[i.first])//枚举与mp[fx][i]满足条件的值出现在y中值
            {
                if(mp[fy].count(j))
                    ans+=1LL*i.second*mp[fy][j];
            }
        }
        for(auto i:mp[fx])//树的合并
            mp[fy][i.first]+=i.second;
        fa[fx]=fy;
    }
    void update(int x,int y)
    {
        int fx=Find(x);
        for(auto i:num[a[x]])
            ans-=mp[fx][i];
        mp[fx][a[x]]--;
        for(auto i:num[y])
            ans+=mp[fx][i];
        mp[fx][y]++;
        a[x]=y;
    }
    int main()
    {
        int n,m;
        ans=0;
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            fa[i]=i;
            mp[i][a[i]]=1;
        }
        int op,x,y;
        while(m--)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==1)
            {
                a[x]=y;
                fa[x]=x;
                mp[x][a[x]]=1;
            }
            else if(op==2)
                join(x,y);
            else
                update(x,y);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    L. Coordinate Paper

    分析

    首先,只要确定了 (a_i) ,就可以确定 ((sum_{i=1}^{n}{a_i})mod (k+1)) 的值。因为,如果不考虑 (a_i-a_{i+1}=k) 的情况,有:

    [sum_{i=1}^{n}{a_i}=a_1 imes n+frac{(n-1) imes n}{2} ]

    再考虑 (a_i-a_{i+1}=k) 的情况,就在上式的基础上减去了 (x imes (k+1)(xgeq 0))

    即:

    [sum_{i=1}^{n}{a_i}=a_1 imes n+frac{(n-1) imes n}{2}-x imes(k+1)(xgeq 0) ]

    因此,对于一个确定的 (a_1\% (k+1)) ,可以在 (O(1)) 的时间内确定 (sum_{i=1}^{n}{a_i}mod (k+1)) 的值,此时的序列一定为如下形式:

    [a_1,a_1+1,k,cdots,0,1,2,cdots,k,0,1,2,cdots ]

    此时,对于一个确定的 (a_1) ,该序列有 (minsum_{i=1}^{n}{a_i})

    如果满足:

    [Sgeq min sum_{i=1}^{n}{a_i} 且 S\% (k+1)=sum_{i=1}^{n}{a_i}\%(k+1) ]

    则必然可以构造出满足要求的序列。

    因为可以不断地在满足 (sum_{i=1}^{n}{a_i}) 最小的序列中不断的加 ((k+1)) ,使得最后的和为 (S)。加的时候,先对 (0) 加,不够再对 (1) 加,以此类推,因为要满足题目给的相邻两个数的大小条件。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    ll a[N];
    int num[N];
    int main()
    {
        int n,k,f=0;
        ll s,tmp=0;
        scanf("%d%d%lld",&n,&k,&s);
        if(n==1)
        {
            printf("%lld
    ",s);
            return 0;
        }
        for(int i=0;i<=k;i++)
        {
            ll d=1LL*k*(k+1)/2;
            ll m=(n-(k-i+1))/(k+1);//完整区间的个数
            int x=n-(k-i+1)-m*(k+1);//后面剩余部分的个数
            tmp=1LL*(i+min(k,i+n-1))*(min(k,i+n-1)-i+1)/2+d*m;
            if(x>0)
                tmp+=1LL*(0+x-1)*x/2;
            if(s>=tmp&&s%(k+1)==tmp%(k+1))
            {
                f=1;
                a[1]=i;
                break;
            }
        }
        if(f==0)
            printf("-1
    ");
        else
        {
            num[a[1]]++;
            for(int i=2;i<=n;i++)
            {
                a[i]=(a[i-1]+1)%(k+1);
                num[a[i]]++;
            }
            ll res=0,y=(s-tmp)/(k+1);
            ll r=y%n,w=y/n;
            int p=-1;
            for(int i=0;i<=k;i++)
            {
                res+=num[i];
                if(res>r)
                {
                    p=i;
                    res=r-(res-num[i]);
                    break;
                }
            }
            for(int i=1;i<=n;i++)
            {
                if(a[i]<p) a[i]+=1LL*(k+1)*(w+1);
                else if(a[i]>p) a[i]+=1LL*(k+1)*w;
                else if(a[i]==p)
                {
                    if(res>0) a[i]+=1LL*(k+1)*(w+1),res--;
                    else a[i]+=1LL*(k+1)*w;
                }
            }
            for(int i=1;i<=n;i++)
                printf("%lld%c",a[i],i==n?'
    ':' ');
        }
        return 0;
    }
    //10 2 55
    //2 10 20
    
  • 相关阅读:
    python定时任务:apscheduler的使用(还有一个celery~)
    Python定时任务-schedule vs. Celery vs. APScheduler
    结合Django+celery二次开发定时周期任务
    The Django Book 2.0--中文版
    第十二章: 部署Django
    Django扩展自定义manage命令
    使用django-extension扩展django的manage――runscript命令
    Django | 执行项目下指定的脚本
    C语言宏定义技巧——多次包括头文件内容不同
    《Java并发编程实战》第十章 避免活跃性危急 读书笔记
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/13955662.html
Copyright © 2020-2023  润新知