一些杂题【2022-07-16 更新】

· · 个人记录

大致按照难度排序。

“自评难度”与当时状态存在一定关联,仅供参考。

ARC103A /\/\/\/

来源:AtCoder
难度:826,绿
自评难度:5,灰

放这题只是为了凑齐 ARC103 的 4 题。

Code:

ll n,v[100005],cnt[100005],a1,as1,a2,as2,t1,ts1,t2,ts2;
int main()
{
    n=rd();for(int i=1;i<=n;++i)v[i]=rd();
    clr(cnt,100001);
    for(int i=1;i<=n;i+=2)++cnt[v[i]];
    for(int i=1;i<=100000;++i)
    {
        if(cnt[i]>cnt[a1])as1=a1,a1=i;
        else if(cnt[i]>cnt[as1])as1=i;
    }
    t1=cnt[a1];ts1=cnt[as1];
    clr(cnt,100001);
    for(int i=2;i<=n;i+=2)++cnt[v[i]];
    for(int i=1;i<=100000;++i)
    {
        if(cnt[i]>cnt[a2])as2=a2,a2=i;
        else if(cnt[i]>cnt[as2])as2=i;
    }
    t2=cnt[a2];ts2=cnt[as2];
    if(a1^a2)prt(n-t1-t2);
    else prt(umin(n-t1-ts2,n-ts1-t2));
    ret 0;
}

IMO2019T5

来源:IMO
难度:无
自评难度:1.00

这种题对 OIer 来说还是水了点(

虽说 Atziluth 特别菜,给的证明不一定严谨什么的(

(a) 证明:

不妨设当前硬币正面朝上的位置集合为 S。此时 S 中必然存在至少一枚硬币为从左往右第 |S| 个硬币或其右侧的硬币。

接下来在从左往右第 |S| 个硬币上放置一个标记。不难发现下一次操作必然在标记对应的硬币上执行。

容易发现经过若干次操作后,标记会回到原位,同时原序列中正面朝上的数量会减少 1,即对于所有非空的 S 都能在若干次操作后到达一个 S' 满足 |S'|=|S|-1 。即所有 S 都能在若干次操作后到达 \varnothing\Box

(b)

记标记从一个位置向右移直到将一个正面翻为翻面并最后回到出发点为一轮过程。设当前存在 k 枚硬币为正面,第 k 枚硬币右侧(包括第 k 枚本身)的第一枚正面朝上的硬币位置为 p,则这一轮过程的操作次数为 2p-2k+1。将其拆为 2p-1-(2k-2) 并分别求期望:

\begin{aligned} Ans&={\sum_{i=1}^{n} (2i-1) \over 2}-{\sum_{i=1}^{n}\binom{n}{i}\sum_{j=1}^{i}(2j-2) \over 2^n} \\&={n^2 \over 2}-{\sum_{i=2}^{n}i(i-1)\binom{n}{i} \over 2^n} \end{aligned} \begin{aligned} &i(i-1)\binom{n}{i} \\=&2\binom{n}{i}\binom{i}{2} \\=&2\binom{n}{2}\binom{n-2}{i-2} \end{aligned}

代入得:

\begin{aligned} Ans&={n^2 \over 2}-{\sum_{i=2}^{n}2\binom{n}{2}\binom{n-2}{i-2} \over 2^n} \\&={n^2 \over 2}-{2\binom{n}{2}\sum_{i=2}^{n}\binom{n-2}{i-2} \over 2^n} \\&={n^2 \over 2}-{n(n-1) \over 4} \\&={n(n+1) \over 4} \end{aligned}

ARC139B Make N

来源:AtCoder
难度:1719,蓝
自评难度:400,棕

根号分治入门题,对性价比高的物品的体积分治即可。

蓝和蓝不能一概而论,我曾经在极端愤怒的情况下……被 Ring MST 吊着打。

Code:

ll T,n,a,b,x,y,z;cst ll lim=31000;
int main()
{
    T=rd();
    while(T --> 0)
    {
        n=rd();a=rd();b=rd();x=rd();y=rd();z=rd();ll ans=n*x;
        tomin(y,a*x);tomin(z,b*x);
        if(z*a>y*b)swap(a,b),swap(y,z);
        //cerr<<1<<' '<<x<<' '<<a<<' '<<y<<' '<<b<<' '<<z<<endl;
        if(b>=lim)for(ll i=0;i<=n;i+=b)tomin(ans,i/b*z+(n-i)/a*y+(n-i)%a*x);
        else for(ll i=0;i<=umin(a*b-a,n);i+=a)tomin(ans,i/a*y+(n-i)/b*z+(n-i)%b*x);
        prt(ans);
    }
    ret 0;
}

AGC030C Coloring Torus

来源:AtCoder
难度:3207,Cu
自评难度:1200,青

$k$ 为 $4$ 的倍数时把上述做法改成每行两种数字相间即可。 其余情况考虑旋转 $45\degree$,即填铺满后的对角线。例($k=7$): ``` 1 4 5 7 3 6 7 2 5 7 1 4 7 2 3 6 ``` 注意取的 $n$ 必须为偶数,如 $k=5$: ``` 1 3 4 5 3 4 5 2 4 5 1 3 5 2 3 4 ``` Code: ```cpp ll n,k,ans[505][505]; il void apply(int a,int b,int p) { int cur=a; for(int i=1;i<=n;++i) { ans[p][i]=cur; cur=a^b^cur,p=(p+n-2)%n+1; } } int main() { k=rd(); if(k<=500) { prt(k);for(int i=1;i<=k;++i)for(int j=1;j<=k;++j) prt(i," \n"[j==k]); } else { n=(k+1)>>1;n+=n&1;prt(n);ll r=k-n,cur=0; for(int i=1;i<=n;++i) { if(r)apply(cur+1,cur+2,i),cur+=2,--r; else apply(cur+1,cur+1,i),++cur; } for(int i=1;i<=n;++i)for(int j=1;j<=n;++j) prt(ans[i][j]," \n"[j==n]); } ret 0; } ``` ## ARC103C Tr/ee > 来源:AtCoder > 难度:1722,蓝 > 自评难度:1400,青 一场比赛出两道这种题可还行。 无解即 $s_1=0$ 或 $\{s_{0..n}\}$ 不是回文串。 其余情况根据每一个 `1` 后面的第一个 `1` 连边即可。具体见代码。 Code: ```cpp ll n,s[100005];char str[100005]; int main() { scanf("%s",str+1);n=strlen(str+1);for(int i=1;i<=n;++i)s[i]=str[i]&1; if(!s[1])ret prt(-1),0; for(int i=0;i<=n;++i)if(s[i]^s[n-i])ret prt(-1),0; for(int i=2,lst=1;i<=n;++i) { if(s[i]) { for(int j=lst;j<i;++j)prt(j,' '),prt(i); lst=i; } } prt(n-1,' ');prt(n); ret 0; } ``` ## ARC103D Distance Sums > 来源:AtCoder > 难度:2836,红 > 自评难度:1500,青 显然点权最小的是重心,最大的是一个叶子节点。将点权排序,然后从最大的节点往前找,过程中动态维护已知点的父节点和子树大小即可。 坑点:因为建树依靠的是相对关系,所以建完树后需要回带检验点权是否合法。 Code: ```cpp ll n,fa[100005],siz[100005],dis[100005];pr d[100005]; il bool calc() { ll cnt=0; for(int i=n;i>=2;--i) { ll id=d[i].sec,v=d[i].fir-n+2*siz[id]; if(v>=d[i].fir){++cnt;continue;} int pos=lower_bound(d+1,d+1+n,prpr(v,0ll))-d; if(d[pos].fir==v)fa[id]=d[pos].sec,siz[fa[id]]+=siz[id]; else ++cnt; } ret !cnt; } il bool j() { ll t=0;for(int i=1,id;i<=n;++i) if(fa[id=d[i].sec])t+=dis[id]=dis[fa[id]]+1; ret t==d[1].fir; } int main() { n=rd();for(int i=1;i<=n;++i)d[i]=prpr(rd(),i),siz[i]=1; sort(d+1,d+1+n);if(!calc()||!j())ret prt(-1),0; for(int i=1;i<=n;++i)if(fa[i])prt(fa[i],' '),prt(i); ret 0; } ``` ## ARC139C One Three Nine > 来源:AtCoder > 难度:2406,橙 > 自评难度:1600,蓝 ~~赛时因为一直在做 D/E 而没做出这道水题。2800 就这么没了,大家一定要引以为戒。~~ 首先不难发现 $n,m \le 3$ 时答案为 $nm$。下设 $3 \le n \le m$。 因为 $3x+y=k$ 的直线有 $3n+m-3$ 条,所以答案的上界是 $3n+m-3$。 我们画出一堆 $3x+y=k$ 和 $x+3y=k$ 的直线,显然每条直线上至多有一个点。于是不难想到 $y=x$。 具体而言,将 $3 \times 3$ 放在这根线上。如图(迫真): | | | | | o | o | | :----------: | :----------: | :----------: | :----------: | :----------: | :----------: | | | | **o** | **o** | **o** | **o** | | | | **o** | **o** | **o** | | | **o** | **o** | **o** | **o** | **o** | | | **o** | **o** | **o** | | | | | **o** | **o** | **o** | | | | 放完之后放 $(n,m),(n,m-1),\cdots$ 直至不能放即可。 显然,对于 $2\nmid n$,其答案为 $3n+m-3$,已经达到上界。对于 $2|n$,其答案为 $3n+m-4$,下面说明其正确性(不会严谨证明): 首先写一个 $\Theta(2^{nm})$ 的暴力得到 $n=m=4$ 时答案为 $12$,又因所有 $2|n$ 本质相同,所以答案正确。 Code: ```cpp ll n,m,lim;bool rev=0;vector<pr>ans;bool vis[400005]; il bool ins(ll x,ll y) { if(x<1||x>n||y<1||y>m||vis[x*3+y])ret 0; vis[x*3+y]=1;ans.pb(prpr(x,y));ret 1; } int main() { n=rd();m=rd();if(n>m)swap(n,m),rev=1; for(int i=1;i<=n;i+=2)for(int j=0;j<3;++j)for(int k=0;k<3;++k)ins(i+j,i+k); for(int i=m;i>=1;--i)if(!ins(n,i))break; prt(ans.size());for(auto x:ans)if(rev)prt(x.sec,' '),prt(x.fir);else prt(x.fir,' '),prt(x.sec); ret 0; } ``` ## AGC022B GCD Sequence > 来源:AtCoder > 难度:1645,蓝 > 自评难度:1700,蓝 首先放两个互质的数,然后条件转换为: - $\forall 1 \le i \le n,gcd(a_i,S)>1$; - $a_i \le 30000$。 先放 `2,3,4,9`,接下来保证和为 `6` 的倍数即可。同时放 $6k+3,6k+9$ 或 $6k+2,6k+4$,最后放 $6k$。 Code: ```cpp ll n,ans[20005],t; queue<ll>a,b,c; il void apply(queue<ll>&q){ans[++t]=q.front();q.pop();} int main() { n=rd();if(n==3)ret puts("2 5 63"),0; ans[++t]=2;ans[++t]=4;ans[++t]=3;ans[++t]=9;if(n&1)ans[++t]=6; for(int i=15;i<=30000;i+=6)a.push(i); for(int i=12;i<=30000;i+=6)b.push(i); if(!(n&1))b.push(6); for(int i=8;i<=30000;i+=6)c.push(i),c.push(i+2); while(t<n-1) { if(a.size()>1)apply(a),apply(a); if(c.size()>1)apply(c),apply(c); if(b.size()>1)apply(b),apply(b); } if(t<n)apply(b); sort(ans+1,ans+1+n);for(int i=1;i<=n;++i)prt(ans[i]," \n"[i==n]); ret 0; } ``` ## 1227G Not Same > 来源:CodeForces > 难度:2600 > 自评难度:2000 [Link](https://www.luogu.com.cn/blog/224991/solution-cf1227g) ## AGC035C Skolem XOR Tree > 来源:AtCoder > 难度:2332,黄 > 自评难度:1800,蓝 在这种题上卡了这么久,丢人。 两个显然的结论: $$(4k) \mathrm{xor} (4k+1) \mathrm{xor} (4k+2) = (4k+3)$$ $$0=a \mathrm{xor} a$$ 方便起见,把点分成 $1...n$ 和 $n+1...2n$ 两组。并将第一组按每 $4$ 个分(特别的, $1,2,3$ 分成一块)。奇数情况方案为 `Z` 型,即类似于 $4 \to 5 \to 6 \to 7 \to n+4 \to ...$。 偶数情况把 $n$ 和 $2n$ 拆下来,把 $n$ 挂在 $2n-2$ 上,$2n$ 挂在 $n+2\mathrm{lowbit}(n)-1$ 上。 做完了。 Code: ```cpp ll n; int main() { n=rd();if(!(n&(n-1)))ret puts("No"),0; puts("Yes");int l=n-!(n&1); for(int i=3,t=1;i<=l;t=i+1,i+=4) { for(int j=t;j<i;++j)prt(j,' '),prt(j+1); prt(t+n,' '),prt(i); } if((l&3)^3) { for(int j=((n>>2)<<2);j<l;++j)prt(j,' '),prt(j+1); prt(n+l-3,' '),prt(l); } for(int i=1;i<l;++i)prt(n+i,' '),prt(n+i+1); if(!(n&1)) { if(n&3)prt(n-1,' '),prt(n),prt(n+n-5,' '),prt(n+n); else prt(n+n-2,' '),prt(n),prt(n+2*(n&(-n))-1,' '),prt(n+n); } ret 0; } ``` ## AGC031C Differ by 1 Bit > 来源:AtCoder > 难度:2217,黄 > 自评难度:2200,黄 ???洛谷上难度是绿???[脏话删除] [该用户因暴戾语言被禁赛一年] 原问题等价于求 $2^n$ 个点,每个点度数都是 $n$ 的图的哈密尔顿回路。 把图丢到 [Graph Editor](https://csacademy.com/app/graph_editor) 能得出一些结论: - 对于每一个 $2^n$ 个点的图,其为两个相同的 $2^{n-1}$ 个点的图对应点相连得到。 - 这个图是二分图,有解当且仅当起点与终点异或和的位数为奇数。 于是分治构造即可。设当前在第 $v$ 维度(维度指 $\log_2|V|$): - 若起点与终点在第 $v$ 维度的坐标均为 $x_v$,则从任意一个与终点相邻且坐标为 $x_v$ 的点进入坐标为 $x_v\ \mathrm{xor}\ 1$ 的所有点并从终点的投影离开,到达终点。 - 若坐标不同,则先扫到起点编号 $\mathrm{xor}\ 1$ 的位置,然后进入终点所在子图。 为方便起见,钦定起点为 `0`,其余情况移动结束后给答案序列对应位置全部异或上起点编号即可。 Code: ```cpp ll n,a,b,ans[200005],t; il ll bitcnt(ll x){ll res=0;while(x)++res,x&=x-1;ret res;} il void slv(ll s,ll d,int v) { ll lst=t,tmp=1<<(v-1);d^=s; if(v==1)ans[++t]=0,ans[++t]=d; else if(d&tmp)slv(0,1,v-1),slv(tmp+1,d,v-1); else slv(0,d,v-1),--t,slv(tmp+ans[t],tmp+d,v-1),ans[++t]=d; for(int i=lst+1;i<=t;++i)ans[i]^=s; } int main() { n=rd();a=rd();b=rd(); if(bitcnt(a^b)&1) { puts("YES");slv(a,b,n); for(int i=1;i<=t;++i)prt(ans[i]," \n"[i==t]); } else puts("NO"); ret 0; } ``` ## ARC144 K Derangement > 来源:AtCoder > 难度:1622,蓝 > 自评难度:2300,黄 Totally trash. 题出的很好,下次别出了。 Code: ```cpp ll n,k,a[300005],c,lb,la; int main() { n=rd();k=rd();lb=k*4,la=k*3;if(k+k>n)ret prt(-1),0; //for(int i=1;i<=n;++i)a[i]=(i-1+k)%n+1; c=1;while(n-c+1>=lb) { for(int i=c;i<c+k;++i)a[i]=i+k; for(int i=c+k;i<c+k+k;++i)a[i]=i-k; c+=k+k; } cerr<<c<<endl; if(n-c+1<=la) { int a=c,b=c+k; while(b<=n)::a[a++]=b++; b=c; while(a<=n)::a[a++]=b++; } else { int r=(n-c+1)%k,tmp=n-k; //cerr<<c<<' '<<r<<' '<<tmp<<endl; for(int i=1;i<=r;++i)a[n-i+1]=tmp--; ++tmp;int x=c,y=c+k; while(y<=n)(a[x++]=(tmp<=y&&y<=tmp+r-1)?y-tmp+c:y),++y; y=c+r;while(x<=n-r)a[x++]=y++; } for(int i=1;i<=n;++i)prt(a[i]," \n"[i==n]); ret 0; } ``` ## ARC131E Christmas Wreath > 来源:AtCoder > 难度:2314,黄 > 自评难度:2400,橙 神仙题。 将边划分成大小为 $1,2,\cdots,n-1$ 的边集,也就是 $\{(1,2)\},\{(1,3),(2,3)\},\cdots$。把这些边集分成和相同的三组分别染色即可。 ```cpp ll n;vector<ll>g[4];char ans[55][55];const char str[]="PRBW"; int main() { n=rd();if(n<=4||(n*(n-1)%3))ret puts("No"),0; int tmp=n;while(tmp>=12)tmp-=6; switch(tmp) { case 6:g[1].assign({1,4}),g[2].assign({2,3}),g[3].pb(5);break; case 7:g[1].assign({1,6}),g[2].assign({2,5}),g[3].assign({3,4});break; case 9:g[1].assign({1,2,3,6}),g[2].assign({4,8}),g[3].assign({5,7});break; case 10:g[1].assign({1,2,3,4,5}),g[2].assign({6,9}),g[3].assign({7,8});break; default:ret puts("No"),0; } tmp=n; while(n>=12) { g[1].pb(n-6);g[2].pb(n-5);g[3].pb(n-4); g[3].pb(n-3),g[2].pb(n-2),g[1].pb(n-1); n-=6; } n=tmp; puts("Yes"); for(int t=1;t<=3;++t) for(auto x:g[t]) for(int j=x;j>=1;--j) ans[j][x+1]=str[t]; for(int i=1;i<n;++i,pc('\n'))for(int j=i+1;j<=n;++j)pc(ans[i][j]); ret 0; } ``` ## ARC103B Robot Arms > 来源:AtCoder > 难度:2677,橙 > 自评难度:2400,橙 神仙题。 对 $x,y$ 做二进制拆分。具体而言: - 所有 $(x+y)\text{ is odd}$ 时指定 $c$ 为一堆 $2$ 的幂; - 所有 $(x+y)\text{ is even}$ 时指定 $c$ 为一堆 $2$ 的幂多一个 $1$; - $x+y$ 不同奇偶性时无解。 之后贪心,每次选择 $|x|,|y|$ 中较大的减去当前的 $c$。 Code: ```cpp ll n,x[1005],y[1005];vector<ll>c; int main() { n=rd();bool f0=0,f1=0; for(int i=1;i<=n;++i)x[i]=rd(),y[i]=rd(),((x[i]+y[i])&1)?(f1=1):(f0=1); if(f0&&f1)ret prt(-1),0; for(int i=35;i>=0;--i)c.pb(1ll<<i); if(f0)c.pb(1); prt(c.size());for(auto j:c)prt(j,' '); pc('\n'); for(int i=1;i<=n;++i) { for(auto j:c) { if(uabs(x[i])>uabs(y[i])) { if(x[i]>0)pc('R'),x[i]-=j; else pc('L'),x[i]+=j; } else { if(y[i]>0)pc('U'),y[i]-=j; else pc('D'),y[i]+=j; } } pc('\n'); } ret 0; } ```