Codeforces Round #836 (Div. 2)

Preface

补题,上周末没比赛很难受啊,而且这周要考CET-4,这周的模考听力只错了2pts,感觉自信满满flag~~

嘛值得一提的是学校还是沦陷了,让我们自愿返乡了

但是我知道以我的自制力现在回去明年来缓考肯定是寄的,所以在得知学校大概率有阳的情况下继续留守

嘛感觉不管回不回去对我没太大影响的说

这场一晚上只做了ABCD,主要是D我写完自己感觉有问题在改,结果交上去直接过了?


A. SSeeeeiinngg DDoouubbllee

SB题

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
int t,n,c[26]; char s[105];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) ++c[s[i]-'a'];
		for (i=0;i<26;++i) for (j=1;j<=c[i];++j) putchar(i+'a');
		for (i=25;~i;--i) for (j=1;j<=c[i];++j) putchar(i+'a');
		for (putchar('\n'),i=0;i<26;++i) c[i]=0;
	}
	return 0;
}

B. XOR = Average

好像我的构造方法很奇怪,让我构思了挺久

首先我们发现当\(n\)为奇数时直接令所有数等于\(1\)即可,接下来浅显地考虑下\(n=2\)的情况,发现用1 3即可

然后我们发现这样当\(n\)质因数分解后仅有一个\(2\)的话,我们只要用\(\frac{n}{2}\)1 3即可

这就启发了我们,我们只要找出所有\(2^k\)的分法,然后让\(\frac{n}{2^k}\)为奇数构造即可

手玩一下不难发现\(n=4\)的时候可以用3 3 3 7来构造,同理\(n=8\)7 7 7 7 7 7 7 15

以此类推,\(2^k\)的话就是由\(2^k-1\)\(2^k-1\)和一个\(2^{k+1}-1\)构成

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
int t,n;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; scanf("%d",&n); int p=1,tmp=n;
		while (!(tmp&1)) tmp>>=1,p<<=1;
		for (i=1;i<=n/p;++i) for (printf("%d ",(p<<1)-1),j=1;j<p;++j)
		printf("%d ",p-1); putchar('\n');
	}
	return 0;
}

C. Almost All Multiples

刚开始没看到字典序最小的限制,以为是随便输一种(那不是SB题嘛)

然后随便写了个发现一直WA,后来去仔细看了眼题目才发现是字典序最小

首先我们发现用\(n\)放在\(a_x\)上,其它\(2\sim n-1\)都等于自己是一定可行的,那么我们考虑优化字典序

不难发现\(2\sim x-1\)的数不能变,因为它们已经足够小

那么考虑在\(x+1\sim n-1\)中找一个数可以替换\(x\),不难发现这个数\(y\)要满足\(x|y\and y|n\)

同时如果用\(y\)替换了\(a_x\)那后面依然可以如法炮制,看在后面能否找一个数替换\(a_y\)即可

重复这个过程即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,x,ans[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%d",&n,&x); ans[1]=x; ans[n]=1;
		if (n%x) { puts("-1"); continue; }
		if (n==x)
		{
			for (printf("%d ",n),i=2;i<n;++i) printf("%d ",i);
			printf("1\n"); continue;
		}
		for (i=2;i<x;++i) ans[i]=i; int cur=x; for (i=x+1;i<n;++i)
		if (i%cur==0&&n%i==0) ans[cur]=i,cur=i; else ans[i]=i;
		for (ans[cur]=n,i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
	}
	return 0;
}

D. Range = √Sum

莫名奇妙的乱搞做法,但就是能过的说

首先一个naive的想法就是确定\(x\),令\(\sqrt {\sum_{i=1}^n a_i}=x\),然后考虑确定\(y\)作为最小数,\(y+x\)作为最大数

不难发现这样我们只要在剩下的\([y+1,y+x-1]\)中找出\(n-2\)不相同的数,使得这\(n-2\)个数的和等于\(x^2-y-(y+x)\)即可

\([y+1,y+x-1]\)取数是存在范围的,如果拿最小的\(n-2\)个就是\((n-2)\times (y+1)+\frac{(n-3)(n-2)}{2}\),拿最大的\(n-2\)个就是\((n-2)\times (y+x-1)-\frac{(n-3)(n-2)}{2}\)

不难发现在这中间的任意一个数都能被表示出来,那么我们现在就考虑怎样在确定\(x\)的情况下求出\(y\),即要求不等式成立:

\[(n-2)\times (y+1)+\frac{(n-3)(n-2)}{2}\le x^2-y-(y+x)\le (n-2)\times (y+x-1)-\frac{(n-3)(n-2)}{2} \]

\[\frac{x^2+(1-n)\times x+\frac{1}{2}n^2-\frac{3}{2}n+1}{n}\le y\le \frac{x^2-x-\frac{1}{2}n^2-\frac{3}{2}n+1}{n} \]

那么我们只要枚举\(x\),然后看是否存在一个整数\(y\)满足上式即可

\(x,y\)都确定后构造答案就非常简单了,先设\(n-2\)个数都取最小的

然后从后往前考虑每个数,差多少就往后移即可,如果不够的话就移到当前没用过的最大的数,这个具体看代码很好理解

综上,虽然我们不知道枚举\(x\)的上界是多少,但是根据直觉这个东西应该不大(毕竟区间的变化是平方级别的),因此可以通过

#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=300005;
int t,n,ans[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; if (scanf("%d",&n),n==2) { puts("1 3"); continue; }
		for (i=n-1;;++i)
		{
			int L=ceil((1.0*i*i+1.0*i*(1-n)+0.5*n*n-1.5*n+1)/n);
			int R=floor((1.0*i*i-i-0.5*n*n-1.5*n+1)/n); if (L>R) continue;
			long long sum=1LL*(n-2)*(L+1)+1LL*(n-3)*(n-2)/2LL;
			long long tar=1LL*i*i-i-2LL*L; int lim=L+i-1;
			for (j=2;j<=n-1;++j) ans[j]=L+j-1;
			for (j=n-1;j>=2&&sum<tar;--j)
			if (lim-ans[j]>=tar-sum) ans[j]+=tar-sum,sum=tar;
			else sum+=lim-ans[j],ans[j]=lim,--lim;
			ans[1]=L; ans[n]=L+i; break;
		}
		for (i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
	}
	return 0;
}

E. Tick, Tock

意外的simple啊这题

首先不难发现我们可以先单独考虑每一列\(j\),对于其中两个不同行且有初始值的元素\(a_{p,j},a_{q,j}\),我们显然可以确定下第\(p\)行和第\(q\)行的相对操作次数

那么我们把所有这样的关系建成一个图,每一行就是一个点,两点\(p,q\)之间的边权就是上面提到的两个对应元素的差值

那么我们只需要通过一个DFS就能判断是否合法了,下面考虑求出答案的个数

不难发现如果所有点都在一个联通块里,答案是唯一的

那么考虑如果多了一个联通块,那么显然此时答案为\(h\),因为同一个联通块内的状态其实都是紧密统一的,整个联通块的方案数乘上\(h\)就相当于所有位置的方案乘上\(h\)

因此设联通块个数为\(cnt\),则答案为\(h^{cnt-1}\)

但是有一个细节就是一整列都是空的这种情况,此时这一列的取法可以任意的整体增减一个数

因此设空列的个数为\(empty\),答案再乘上\(h^{empty}\)即可

#include<cstdio>
#include<utility>
#include<vector>
#define RI register int
#define CI const int&
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=200005,mod=1e9+7;
typedef pair <int,int> pi;
int t,n,m,h,d[N]; vector <int> a[N]; vector <pi> v[N]; bool flag;
inline void DFS(CI now,CI sum)
{
	d[now]=sum; for (auto it:v[now])
	if (!~d[it.fi]) DFS(it.fi,(sum+it.se)%h);
	else if (d[it.fi]!=(sum+it.se)%h) flag=0;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; for (scanf("%d%d%d",&n,&m,&h),i=1;i<=n;++i)
		d[i]=-1,a[i].resize(m+1),v[i].clear();
		for (i=1;i<=n;++i) for (j=1;j<=m;++j) scanf("%d",&a[i][j]);
		int cnt=0; for (j=1;j<=m;++j)
		{
			bool all_empty=1; int pre=-1;
			for (i=1;i<=n;++i) if (~a[i][j])
			{
				if (~pre)
				{
					v[pre].pb(mp(i,(a[i][j]-a[pre][j]+h)%h));
					v[i].pb(mp(pre,(a[pre][j]-a[i][j]+h)%h));
				}
				all_empty=0; pre=i;
			}
			if (all_empty) ++cnt;
		}
		for (flag=1,i=1;i<=n;++i) if (!~d[i]) ++cnt,DFS(i,0);
		if (!flag) { puts("0"); continue; }
		int ans=1; for (i=1;i<=cnt-1;++i) ans=1LL*ans*h%mod;
		printf("%d\n",ans);
	}
	return 0;
}

Postscript

F怎么又是3000分的题目啊,看都不敢看一眼

我现在感觉自己可能已经阳了,这周末准备润回家了

学校的nt决策导致现在电专已经沦陷了,而且决定留校考试的都成了小丑

唉只能是世事难料,命途多舛啊

锦城虽云乐,不如早还家。

posted @ 2022-12-07 22:23  空気力学の詩  阅读(34)  评论(0编辑  收藏  举报