Codeforces Round 897 (Div. 2)
Preface
这场周一晚上因为沉迷玩《Chaos Child》就没打,赛后点开一看人傻了怎么E2都有1k+的人过
后面补了下发现确实全是丁真题,早知道给小号上上分了
A. green_gold_dog, array and permutation
签到题,把大的\(a_i\)和小的数匹配即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=40005;
int t,n,p[N]; pi a[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i].fi),a[i].se=i;
for (sort(a+1,a+n+1),i=1;i<=n;++i) p[a[i].se]=n-i+1;
for (i=1;i<=n;++i) printf("%d%c",p[i]," \n"[i==n]);
}
return 0;
}
B. XOR Palindromes
首先不难发现要按串长为奇数和偶数分别考虑,不妨先考虑偶数的情况
统计出每对对应位置中相同的数量\(s\)以及不同的数量\(d\),则\(d,d+2,d+4,\cdots,d+2s\)次操作都是可行的
而奇数的情况由于中间那个数可以随便操作,因此\(d,d+1,d+2,\cdots,d+2s+1\)次操作都可行
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,ans[N]; char s[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; int c[2]={0,0}; scanf("%d%s",&n,s+1);
for (i=1;i<n-i+1;++i) ++c[s[i]==s[n-i+1]];
for (i=0;i<=n;++i) ans[i]=0;
if (n&1) for (i=c[0];i<=c[0]+2*c[1]+1;++i) ans[i]=1;
else for (i=c[0];i<=c[0]+2*c[1];i+=2) ans[i]=1;
for (i=0;i<=n;++i) putchar(ans[i]+'0'); putchar('\n');
}
return 0;
}
C. Salyg1n and the MEX Game
观察游戏过程会发现Bob删了什么数我们就补什么数,这个策略一定是最优的
而第一次操作的话为了增大序列的\(\operatorname{MEX}\),我们求出原序列的\(\operatorname{MEX}\)并加入即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,s[N];
inline int ask(CI x)
{
printf("%d\n",x); fflush(stdout);
int t; scanf("%d",&t); return t;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&s[i]);
int mex=n; for (i=1;i<=n;++i) if (s[i]!=i-1) { mex=i-1; break; }
while (~(mex=ask(mex)));
}
return 0;
}
D. Cyclic Operations
稍微手玩一下就会了的题
不妨把\(i\to b_i\)的边建出来,根据外向树的性质我们知道每个连通块一定由一个环收尾
根据题目的要求不难发现最后每个环的大小都必须是\(k\),而除此之外并没有其它要求了
因为在环前面的链我们可以每次让其中的最靠前的数放对,然后整体向后再放对下一个数,直到遇到环
实现的话就先跑个拓扑排序,然后统计下剩下的环的长度即可
注意特判\(k=1\)的情形
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,k,b[N],deg[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) deg[i]=0;
for (i=1;i<=n;++i) scanf("%d",&b[i]),++deg[b[i]];
if (k==1)
{
bool flag=1; for (i=1;i<=n&&flag;++i) if (i!=b[i]) flag=0;
puts(flag?"YES":"NO"); continue;
}
queue <int> q; for (i=1;i<=n;++i) if (!deg[i]) q.push(i);
while (!q.empty())
{
int now=q.front(); q.pop();
if (!--deg[b[now]]) q.push(b[now]);
}
bool flag=1; for (i=1;i<=n&&flag;++i) if (deg[i])
{
int sz=0,x=i; do
{
deg[x]=0; ++sz; x=b[x];
} while (x!=i);
if (sz!=k) flag=0;
}
puts(flag?"YES":"NO");
}
return 0;
}
E1. Salyg1n and Array (simple version)
话说这题真的水的一批,怪不得被艹穿一大片
首先可以发现\(k\ge \sqrt n\),那么我们可以先把前面的整块的长度为\(k\)的都先问了,考虑剩下的那段该怎么处理
稍微手玩一下可以发现只要把剩下那段覆盖到的所有端点都操作一遍即可,这样总操作次数是\(\frac{n}{k}+k\le 100\),可以通过E1
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2505;
int t,n,k;
inline int ask(CI x)
{
printf("? %d\n",x); fflush(stdout);
int t; scanf("%d",&t); return t;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d%d",&n,&k); int ans=0,m=(n/k-1)*k;
for (i=1;i+k-1<=m;i+=k) ans^=ask(i);
for (i=n-k+1;i>m;--i) ans^=ask(i);
printf("! %d\n",ans); fflush(stdout);
}
return 0;
}
E2. Salyg1n and Array (hard version)
本来看数据范围以为\(57=\sqrt {2500}+\sqrt{50}\),是对剩下一个块做根号分治讨论啥的,后面随便画画发现是个傻逼玩意
对于剩下的长度为\(k'\)的段,它的长度一定是偶数,那么我们把它从中间截断,分别问上述的红色区间以及蓝色区间,不难发现这样得到的结果异或起来恰好就是这段的值
因此只需要\(\frac{n}{k}+2\le 52\)次操作即可
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2505;
int t,n,k;
inline int ask(CI x)
{
printf("? %d\n",x); fflush(stdout);
int t; scanf("%d",&t); return t;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d%d",&n,&k); int ans=0;
for (i=1;i+k-1<=n;i+=k) ans^=ask(i);
if (n%k) ans^=ask((i+n)/2-k+1)^ask(n-k+1);
printf("! %d\n",ans); fflush(stdout);
}
return 0;
}
F. Most Different Tree
今天早上数电课上看了下这题感觉就是个爆搜+树Hash
由于不清楚\(n\)个点的本质不同的有根树个数导致不确定复杂度,但回去写了一发发现直接艹过去了可海星
首先我们不难想到最优策略,找到一个有\(m\)个点的有根树使得这个树和原树的任意一个子树都不同构,然后把剩下的点串成一条链和有根树连接起来即可
不难发现这样得到的答案就是\(m-1\),并且很容易发现不存在比这更小的答案了,因为所有点数\(<m\)的有根树都一定会被有同构的和它对应
那么现在的问题就是怎么找这样的一棵树了,直觉告诉我们最后这样的树的点数一定很少(题解说最多只有\(15\)个点),那么我们直接爆搜所有生成树然后检验即可
这里由于我搜索部分写的比较烂,因此树Hash部分就没写AHU写了异或移位来减少些常数
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1e6+5;
int n,x,y; u64 f[N],g[N]; vector <int> v[N],nv[N]; vector <VI> t[20]; unordered_set <u64> s,rst;
mt19937_64 rnd(random_device{}());
uniform_int_distribution<u64> dist(0,ULLONG_MAX);
const u64 seed=dist(rnd);
inline u64 shift(u64 x)
{
x^=seed; x^=x<<13; x^=x>>7; x^=x<<17; x^=seed; return x;
}
inline void DFS1(CI now=1,CI fa=0)
{
f[now]=1; for (int to:v[now]) if (to!=fa) DFS1(to,now),f[now]+=shift(f[to]);
}
inline void DFS2(CI now=0,CI fa=-1)
{
g[now]=1; for (int to:nv[now]) if (to!=fa) DFS2(to,now),g[now]+=shift(g[to]);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j,k; for (scanf("%d",&n),i=1;i<n;++i)
scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
if (n==2) return puts("1 2"),0;
for (DFS1(),i=1;i<=n;++i) s.insert(f[i]);
vector <int> anc; anc.push_back(-1); t[1].push_back(anc); rst.insert(1ull);
for (i=1;;++i)
{
for (auto it:t[i]) for (j=0;j<i;++j)
{
vector <int> nxt=it; nxt.push_back(j);
for (k=0;k<=i;++k) nv[k].clear();
for (k=1;k<=i;++k) nv[nxt[k]].push_back(k);
if (DFS2(),rst.count(g[0])) continue;
if (!s.count(g[0]))
{
int m=n-(i+1);
for (k=1;k<=m;++k) printf("%d %d\n",k,k+1);
for (k=1;k<=i;++k) printf("%d %d\n",nxt[k]+m+1,k+m+1);
return 0;
} else rst.insert(g[0]),t[i+1].push_back(nxt);
}
}
return 0;
}
Postscript
刚开学事情不多就多补补题吧