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;
}