AGC010

拾起来板刷 AGC 的事情,发现之前刷的基本全忘干净了。毕竟半年前的事情了。

现在也完全没有板刷 AGC 的状态和实力。一点点起手吧。

这一套之前做的差不多了,先对着代码看看当时的思路。

[AGC010A] Addition

普及题。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
int n,cnt;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        cnt^=(x&1);
    }
    if(cnt)puts("NO");
    else puts("YES");
    return 0;
}

[AGC010B] Boxes

听说暴力能过。不过看看正解。

首先每次总和减少 \(\dfrac {n(n-1)}2\),和必须整除这个。然后由于是减等差数列,考虑差分。得到差分数组 \(d\) 后变成一个数减 \(n-1\) 其他数 \(+1\),让你全变成 \(0\)

\(cnt_i\)\(i\) 开头操作次数,\(M\) 为总操作次数。那么

\[di=(M-cnt_i)-cnt_i(n-1)=M-cnt_in \]

\(cnt_i\) 必须是整数。就这两个条件。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#define int long long
using namespace std;
int n,a[100010];
long long sum;
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum+=a[i];
    if(sum%(1ll*n*(n+1)/2)!=0){
        puts("NO");return 0;
    }
    long long cnt=sum/(1ll*n*(n+1)/2);
    bool jud=false;
    a[0]=a[n];
    for(int i=n;i>=1;i--){
        a[i]=a[i]-a[i-1];
        if((cnt-a[i])%n!=0||cnt-a[i]<0){
            puts("NO");return 0;
        }
    }
    puts("YES");
    return 0;
}

[AGC010C] Cleaning

看不懂原来的代码了,不知道写的什么东西。于是重新搞了一份。

由于每个节点只有两种消掉方式:子树两条链选出来消一个,或者传到父亲上。那么设 \(f_x\)\(x\) 节点能传上去的,\(sum_x\) 为儿子 \(f_v\) 之和,那么第二种方式个数显然是 \(f_x\),第一种是 \(sum_x-f_x\)。即有:

\[\frac{sum_x-f_x}2+f_x=a_x \]

\[f_x=2a_x-sum_x \]

确保 \(0\le f_x\le a_x\)\(\max f_v\le a_x\) 即可。还有根节点的 \(f\) 一定是 \(0\)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
struct node{
    int v,next;
}edge[200010];
int n,t,rt,a[100010],head[100010],d[100010],f[100010],sum[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs(int x,int fa){
    if(d[x]==1){
        f[x]=a[x];return;
    }
    int mx=0;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=fa){
            dfs(edge[i].v,x);sum[x]+=f[edge[i].v];mx=max(mx,f[edge[i].v]);
        }
    }
    f[x]=(a[x]<<1)-sum[x];
    if(f[x]<0||f[x]>a[x]||mx>a[x]){
        puts("NO");exit(0);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
        d[u]++;d[v]++;
    }
    if(n==2){printf("%s",a[1]==a[2]?"YES":"NO");return 0;}
    for(int i=1;i<=n;i++)if(d[i]!=1){
        rt=i;break;
    }
    dfs(rt,0);
    if(f[rt])puts("NO");
    else puts("YES");
    return 0;
}

[AGC010D] Decrementing

首先 \(\exist a_i=1\) 那白给。考虑没有 \(1\) 的。

除以 \(\gcd\) 这个操作看起来非常的奇怪。然而我们知道上边那个东西 \(\sum a_i-1\) 只需要看奇偶性,于是我们也来分类讨论一下所有数的奇偶性。

首先如果有奇数个偶数,那么随便选一个偶数 \(-1\) 然后交给对手操作,无论怎样所有数的奇偶性都不会变。容易验证这是先手必胜的。

然后如果是偶数个偶数,这时对我们不优。看看奇数。

如果有超过一个奇数,那白给了。但是如果只有一个,可以把这个奇数减掉然后所有数至少减半,可能有情况翻转。显然至多操作 \(O(\log n)\) 次。算上 \(\gcd\) 的一个 \(\log\) ,总共是 \(O(n\log^2n)\)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,a[100010];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int od=0;
    while(1){
        int cnt1=0,cnt2=0,pos;
        bool jud=false;
        for(int i=1;i<=n;i++){
            if(a[i]==1)jud=true;
            if(a[i]>1&&(a[i]&1)){
                pos=i;cnt1++;
            }
            else if(!(a[i]&1))cnt2++;
        }
        if(jud){
            od^=(cnt2&1);
            puts(od?"First":"Second");
            return 0;
        }
        if(cnt2&1){
            if(!od)puts("First");
            else puts("Second");
            return 0;
        }
        else{
            if(cnt1>1){
                if(!od)puts("Second");
                else puts("First");
                return 0;
            }
            if(cnt1==1){
                a[pos]--;
                int d=0;
                for(int i=1;i<=n;i++)d=gcd(d,a[i]);
                for(int i=1;i<=n;i++)a[i]/=d;
                od^=1;
            }
        }
    }
    return 0;
}

[AGC010E] Rearranging

一场考试的题。

先手任意排列,后手交换互质的数。那么如果把不能相互交换的数之间连边,这样就形成了若干连通块。每个连通块内部相对顺序是不能动的,从大到小归并一下就是答案。

考虑每个连通块的答案。我们要每个连通块字典序尽量小,那么直接找到最小的一个开始哪个小搜哪个就行了。

思路很精妙,实现很简易。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
int n,a[2010],ind[2010],ans[2010];
priority_queue<int>q;
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
vector<int>g[2010],v[2010];
bool vis[2010];
void dfs(int x){
    vis[x]=true;
    for(int to:g[x]){
        if(!vis[to]){
            v[x].push_back(to);ind[to]++;
            dfs(to);
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(gcd(a[i],a[j])!=1)g[i].push_back(j);
        }
    }
    for(int i=1;i<=n;i++)sort(g[i].begin(),g[i].end());
    for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
    for(int i=1;i<=n;i++){
        if(ind[i]==0)q.push(i);
    }
    while(!q.empty()){
        int x=q.top();q.pop();
        ans[++ans[0]]=x;
        for(int to:v[x]){
            ind[to]--;
            if(!ind[to])q.push(to);
        }
    }
    for(int i=1;i<=n;i++)printf("%d ",a[ans[i]]);
    return 0;
}

[AGC010F] Tree Game

另一场考试的题。

首先有结论:每次一定是往更小的点走。如果往不小于的点走,对手永远可以走回来。

于是叶子节点必败,然后就递推就可以了。必败当且仅当不存在必败的儿子。可以 \(O(n^2)\) 扫一遍所有点做根。

/*我现在T3还有一堆FWT 罪证确凿
不过accoders确实好贺就是了
如果不考试就学博弈论和网络瘤
*/
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
struct node{
    int v,next;
}edge[6010];
int n,t,head[3010],a[3010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
bool dfs(int x,int f){
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            if(a[edge[i].v]<a[x]&&!dfs(edge[i].v,x))return true;
        }
    }
    return false;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    bool jud=false;
    for(int i=1;i<=n;i++){
        if(dfs(i,0))printf("%d ",i),jud=true;
    }
    return 0;
}

好多博弈论。

posted @ 2023-03-14 21:42  gtm1514  阅读(17)  评论(0编辑  收藏  举报