Codeforces Round #732 (Div. 2)

Codeforces Round #732 (Div. 2)

Problem A. AquaMoon and Two Arrays

数组\(a_n,b_n\)由非负整数组成,要求一个方案让\(a_n\)按下列规则变化后等于\(b_n\)

选择\((i,j)\)后操作:\(a'_i = a_i - 1 , a'_j = a_j + 1,a'_k(k\ne i,j)=a_k\)

若无法完成任务输出\(-1\)

对于\(100\%\)的数据:\(1 \leq n \leq 100,0\leq a_i,b_i \leq 100\)

显然若\(\sum a_i \ne \sum b_i\)则无法完成,输出\(-1\)

否则,考虑第\(i\)个位置需要加上或者减去多少,需要加上的位置和需要减去的位置逐一配对。

用双指针可以实现。

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=105;
struct rec{
	int val,id;
}d[N],f[N],ans[N];
int a[N],b[N],c[N],n;
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		scanf("%d",&n);
		int sum1=0,sum2=0;
		for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum1+=a[i];
		for (int i=1;i<=n;i++) scanf("%d",&b[i]),sum2+=b[i];
		if (sum1!=sum2) {
			puts("-1"); continue;
		}
		int cnt1=0,cnt2=0;
		for (int i=1;i<=n;i++) {
			c[i]=b[i]-a[i];
			if (c[i]>0) d[++cnt1]=(rec){c[i],i};
			if (c[i]<0) f[++cnt2]=(rec){-c[i],i};
		}
		int pt1=1,pt2=1,tot=0;
		while (pt1<=cnt1||pt2<=cnt2) {
			if (d[pt1].val<f[pt2].val) {
				for (int i=1;i<=d[pt1].val;i++)
					ans[++tot]=(rec){d[pt1].id,f[pt2].id};
				f[pt2].val-=d[pt1].val;
				pt1++;
			} else if (d[pt1].val>f[pt2].val) {
				for (int i=1;i<=f[pt2].val;i++)
					ans[++tot]=(rec){d[pt1].id,f[pt2].id};
				d[pt1].val-=f[pt2].val;
				pt2++;
			} else {
				for (int i=1;i<=d[pt1].val;i++)
					ans[++tot]=(rec){d[pt1].id,f[pt2].id};
				pt1++;pt2++;
			}
		}
		printf("%d\n",tot);
		for (int i=1;i<=tot;i++) {
			printf("%d %d\n",ans[i].id,ans[i].val);
		}	
	}
 
	return 0;
}

Problem B. AquaMoon and Stolen String

给出\(n(n \ is \ odd)\)个长度为\(m\)字符串,抽出一个串\(t\),剩余\(n-1\)个字符串。

\(n-1\)个字符串中可以两个字符串交换相同位置上的字母,后输出。

所有字符串均由小写字母组成,找出被抽出的字符串\(t\)

对于\(100\%\)的数据\(1\leq n \leq 10^5,1\leq m\leq 10^5\)

我们只要统计初始情况下第\(i\)位某字母出现次数,再统计处理后第\(i\)位某字母出现次数

对于一次交换操作,该位上字母总数是不变的,于是只需要将两次出现次数相减

就可以得出被抽出串第\(i\)位上的字母出现情况了,输出即可。

时间复杂度\(O(26\times n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
string s[N],t[N];
int n,m,ch[26];
int main() {
	int T; scanf("%d",&T);
	while (T--) {
		scanf("%d%d",&n,&m);
		for (int i=1;i<=n;i++) {
			cin>>s[i]; 
		}
		for (int i=1;i<=n-1;i++) {
			cin>>t[i];
		}
		for (int i=0;i<m;i++) {
			for (int j=0;j<26;j++) ch[j]=0;
			for (int j=1;j<=n;j++) ch[s[j][i]-'a']++;
			for (int j=1;j<=n-1;j++) ch[t[j][i]-'a']--;
			for (int j=0;j<26;j++) if (ch[j]) {
				putchar(j+'a');
				break;
			}
		}
		puts("");
		fflush(stdout);
	}
	return 0;
}

Problem C. AquaMoon and Strange Sort

数组\(a_n\),将其排序,要求只能相邻交换。

询问是否可能既使数组有序又使每个元素都被交换偶数次。

对于\(100\%\)的数据\(1 \leq n \leq 10^5,1\leq a_i \leq 10^5\)

相邻元素交换的本质就是让数向排序完成的方向移动,移动的次数是偶数次。

可以看出每个数字并不是径直向要求所在位置移动的,有可能需要向反方向移动。

但是,反方向移动的步数必然再次会正向移动直到原点,移动次数一定是偶数次。

下面只需考虑每个数字径直向要求所在位置移动的情况。难点在于重复的数字。

设排序后数组为\(b_n\),一个数字移动次数等于在\(a\)数组中下标和在\(b\)数组中下标之差。

换句话说,数字\(a_i\)要移到\(b_j\)满足\(i,j\)同奇偶。

对于\(a\)中出现的每一个数字,统计下标奇偶出现的次数。

对于\(b\)出现的每一个数字,统计下标奇偶出现的次数。

当且仅当两次统计相同时,则可以满足(构造显然:奇到奇偶到偶),否则则不行。

时间复杂度\(O(n)\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],c[N][2],d[N][2],n;
int main()
{
	int t; scanf("%d",&t);
	while (t--) {
		scanf("%d",&n);
		for (int i=1;i<=n;i++) {
			scanf("%d",&a[i]);
			c[a[i]][i&1]++;
		}
		sort(a+1,a+1+n);
		for (int i=1;i<=n;i++) {
			d[a[i]][i&1]++;
		}
		bool ok=true;
		for (int i=1;i<=n;i++)
			if (c[a[i]][0]!=d[a[i]][0]||c[a[i]][0]!=d[a[i]][0]) {
				ok=false; break;
			}
		puts(ok?"YES":"NO");
		for (int i=1;i<=n;i++) {
			c[a[i]][0]=0; c[a[i]][1]=0;
			d[a[i]][0]=0; d[a[i]][1]=0;
		}		
	}
	return 0;
 } 

Problem D. AquaMoon and Chess

给出一个长度为\(b\)\(01\)\(s\),可以做若干次如下操作:

  • 把第\(i\)位的\(1\)移到第\(i+2\)处(要求\(s[i]=1,s[i+1]=1,s[i+2]=0,1\leq i \leq n-2\)
  • 把第\(i\)位的\(1\)移到第\(i-2\)处(要求\(s[i]=1,s[i-1]=1,s[i-2]=0,3 \leq i \leq n\)

求出在处理过程中有多少种可能的\(s\)的情况。

对于\(100\%\)的数据满足\(1\leq n \leq 10^5\)

上述两种操作的含义是\(110\rightarrow 011 , 011\rightarrow110,1110\rightarrow 1011\)

本质上这是一个\(0/1\)组合分组问题。

基本单位是\(11\)\(0\),(显然单个的\(1\)会永远的存在在它的本来位置)

设组中有\(m\)\(11\)组合(所有的\(1\)属于且仅属于一个组合,eg\(s="01110"\)只有\(1\)个组合\(11\)),有\(n\)\(0\)

插入法思考,将\(n\)\(0\)插入到\(m\)\(11\)组合中有\((m+1)(m+2)...(n+m)/n! = C_{m+n}^{m}\)种方案。

\(C_{m+n}^{m} = \frac{(m+n)!}{n!m!} = (m+n)! \times inv[n!] \times inv[m!] \ mod \ mo\)\(inv[x] = x^{mo-2} mod\ mo\)

时间复杂度\(O(m+n)\)

# include <bits/stdc++.h>
# define int long long
# define pow POW
using namespace std;
const int N=1e5+10;
const int mo=998244353;
bool vis[N];
char s[N];
int pow(int x,int n) {
	int ans=1;
	while (n) {
		if (n&1) ans=ans*x%mo;
		x=x*x%mo;
		n>>=1;
	}
	return ans;
}
int inv(int x) {
	return pow(x,mo-2);
}
signed main() {
	int t; scanf("%lld",&t);
	while (t--) {
		int l,n=0,m=0; scanf("%lld",&l);
		scanf("%s",s);
		for (int i=0;i<l;i++)
			if (s[i]=='0') n++;
		for (int i=1;i<l;i++) 
			if (s[i-1]=='1'&&s[i]=='1'&&!vis[i]&&!vis[i-1]) {
				m++; vis[i]=true; vis[i-1]=true;
			}
		int ans=1;
		for (int i=1;i<=n+m;i++) ans=ans*i%mo;
		for (int i=1;i<=m;i++) ans=ans*inv(i)%mo;
		for (int i=1;i<=n;i++) ans=ans*inv(i)%mo;	
		printf("%lld\n",ans);	
		for (int i=0;i<l;i++) vis[i]=false;	
	}
	return 0;
}

Problem E. AquaMoon and Permutations

定义一个\(n \times n\)矩阵为拉丁矩阵,当且仅当该矩阵每一行、每一列都构成\(1-n\)的排列。

首先给出一个拉丁矩阵\(a[n][n]\),再给出一个相同规模的矩阵\(b[n][n]\)(不一定是拉丁矩阵)

对于每一个\(i\in[1,n]\)都至少存在一个\(k \in [1,n]\)满足\(a[i][k] = b[i][k]\)

将这\(2n\)行抽出\(n\)行构建出一个新矩阵\(c\),求出有几种不同的\(c\),满足它是拉丁矩阵。

对于\(100\%\)的数据\(1 \leq n\leq 500\)

设新组成的\(2n\)\(n\)列的矩阵为\(t[2n][n]\)

显然当这\(2n\)行中的某一行\(i\)存在一个\(k\in [1,n]\)满足\(t[i][k]\)在第\(k\)列中只出现了一次,那么该行一定出现在\(c\)中,而与之有关联的\(j\)(即满足存在一个\(k \in [1,n]\)使\(t[i][k] = t[j][k]\))必然不在\(c\)中。

否则,在剩余的可能出现在\(c\)中的行\(i\)来说,每一个数字\(t[i][j]\)在第\(j\)列中出现且仅出现\(2\)次,否则必然存在一个\(i\)使得\(i\)行中的某个元素\(t[i][k]\)在第\(k\)列中只出现\(1\)次,与题设矛盾。

因此,只需要任取\(1\)行,并把答案乘以\(2\)即可,并删除该行有关联的其他行\(j\)

重复上述过程,我们发现每一行删除并仅删除\(1\)次。

时间复杂度\(O(n^3)\)

# include <bits/stdc++.h>
# define int long long
# define count V
using namespace std;
const int N=505;
const int mo=998244353;
int a[2*N][N],count[N][N],ans[N];
bool vis[2*N];
int Pow(int x,int n) {
	int ans=1;
	while (n) {
		if (n&1) ans=ans*x%mo;
		x=x*x%mo;
		n>>=1;
	}
	return ans;
}
signed main() {
	int t; scanf("%lld",&t);
	while (t--) {
		int n; scanf("%lld",&n);
		for (int i=1;i<=2*n;i++) 
			for (int j=1;j<=n;j++)
				scanf("%lld",&a[i][j]);
		int cnt=0,tot=0;
		for (int i=1;i<=2*n;i++) vis[i]=false;
		while (cnt<n) {
			for (int i=1;i<=n;i++) 
				for (int j=1;j<=n;j++)
					count[i][j]=0;
			for (int i=1;i<=2*n;i++) if (!vis[i]) {
				for (int j=1;j<=n;j++)
					count[j][a[i][j]]++;
			}
			bool cc; int id;
			for (int i=1;i<=2*n;i++) if (!vis[i]) {
				cc=false;
				for (int j=1;j<=n;j++) if (count[j][a[i][j]]==1) {
					cc=true; id=i; break;
				}
				if (cc) break;
			}
			if (cc) ans[++cnt]=id;
			else {
				for (int i=1;i<=2*n;i++) if (!vis[i]) {
					id=i; break;
				}
				ans[++cnt]=id; tot++;
			}
			for (int i=1;i<=2*n;i++) if (!vis[i]) {
				bool flag=true;
				for (int j=1;j<=n;j++) if (a[i][j]==a[ans[cnt]][j]) {
					flag=false; break;
				}
				if (!flag) vis[i]=true;
			}
		}
		printf("%lld\n",Pow(2ll,tot));
		for (int i=1;i<=cnt;i++) printf("%lld ",ans[i]);
		puts("");	
	}
	return 0;
} 

Problem F. AquaMoon and Wrong Coordinate

\(m\)个人从\(x_i\)出发朝着坐标轴正方向匀速移动,

乱序记录某时长为\(k-1\)的连续时间\(0...(k-2)\)各个人的位置。

但是某一时刻的某一数值与理论值不符,请修正。

对于\(100\%\)的数据,\(5 \leq m \leq 1000,7 \leq k \leq 1000\)

考虑连续的两个时刻,位移和\(\sum {x'_i} = \sum x_i + v_i , \sum {x'_i}^2 = \sum({x_i}^2+2v_ix_i + 1)\)

由于匀速直线运动\(v_i\)是常数,所以\(\sum x_i\)是等差数列,\(\sum {x_i}^2\)的差分数组是等差数列。

通过其他行的数据,可以推出错误的行号\(id\),正确的\(\sum x_i,\sum {x_i}^2\)值是多少。

由此可以推出不符的数据。

时间复杂度\(O(mk)\)

# include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,a[N][N],sum1[N],sum2[N],c1[N],c2[N],c3[N];
map<int,int>mp,tt;
signed main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++) 
		for (int j=1;j<=n;j++) {
			scanf("%d",&a[i][j]);
			sum1[i]+=a[i][j];
			sum2[i]+=a[i][j]*a[i][j];
		}	
	for (int i=1;i<=m;i++) {
		c1[i]=sum1[i]-sum1[i-1];
		c2[i]=sum2[i]-sum2[i-1];
		if (i>1) mp[c1[i]]++,tt[c1[i]]=i;
	}
	int id=m;
	for (map<int,int>::iterator it=mp.begin();it!=mp.end();it++) {
		if ((it->second)==1) id=min(id,tt[it->first]);
	}
	for (int i=1;i<=m;i++) c3[i]=c2[i]-c2[i-1];
	int res;
	if (id>=4) res=c3[3]; else res=c3[7]; 
	
	if (id!=2) {
		for (int i=3;i<=m;i++) c3[i]=res;
		for (int i=1;i<=m;i++) c3[i]=c3[i-1]+c3[i];
		for (int i=1;i<=m;i++) c3[i]=c3[i-1]+c3[i];
	} else {
		int d=sum2[m]-sum2[m-1]-sum2[m-1]+sum2[m-2];
		c3[m]=sum2[m]-sum2[m-1];
		for (int i=m-1;i>=2;i--) c3[i]=c3[i+1]-d;
		for (int i=1;i<=m;i++) c3[i]=c3[i-1]+c3[i];
	}
	if (id>=4) res=c1[3]; else res=c1[7];
	if (id!=2) {
		for (int i=3;i<=m;i++) c1[i]=res;
		for (int i=1;i<=m;i++) c1[i]=c1[i-1]+c1[i];
	} else {
		int d=sum1[m]-sum1[m-1]-sum1[m-1]+sum1[m-2];
		c1[m]=sum1[m]-sum1[m-1];
		for (int i=m-1;i>=2;i--) c1[i]=c1[i+1]-d;
		for (int i=1;i<=m;i++) c1[i]=c1[i-1]+c1[i];
	}
	int ans1=id-1,ans2;
	for (int i=1;i<=n;i++) {
		int t=c1[id]-(sum1[id]-a[id][i]);
		if (t*t+sum2[id]-a[id][i]*a[id][i]==c3[id]) {
			ans2=t; break;
		}
	}
	printf("%d %d\n",ans1,ans2);
	fflush(stdout);
	return 0;
}
posted @ 2021-07-14 20:21  Maystern  阅读(63)  评论(0编辑  收藏  举报