abcd做題目名稱真的是
a
題面:有長度為\(n\)的自然數序列\(a\),\(m=\text{mex}(a)\),問是否存在\(l,r,k\)(\(1\le l\le r\le n\wedge k\ge 0\)),使得將\(a_l,a_{l+1},\dots,a_r\)變成\(k\)後滿足\(\text{mex}(a)=m+1\)。
題解:要使\(m+1\)在序列中不存在,且\(m\)在序列中存在,那麼令\(m+1\)出現最早和最晚的位置為\(l\)和\(r\)。再判斷新序列是否存在\(x\in[1,m)\)。
特殊情況:\(\text{mex}(a)=n\),此時不滿足。
時間複雜度\(\Theta(\sum n)\)
程式碼:
#include<cstdio>
#include<cstring>
const int N=1000005;
int T,n,a[N],mex,tmp;
bool vis[N];
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
int main(){
for(freopen("a.in","r",stdin),freopen("a.out","w",stdout),scanf("%d",&T);T--;){
scanf("%d",&n),memset(vis,0,n+2);
for(int i=1;i<=n;i++)scanf("%d",a+i),vis[a[i]]=true;
for(int i=0;i<=n;i++)if(!vis[i]){
mex=i;
break;
}
if(vis[mex+1]){
int l=n,r=1;
for(int i=1;i<=n;i++)if(a[i]==mex+1)l=min(l,i),r=max(r,i);
for(int i=l;i<=r;i++)a[i]=mex;
memset(vis,0,n+2);
for(int i=1;i<=n;i++)vis[a[i]]=true;
for(int i=0;i<=n;i++)if(!vis[i]){
tmp=i;
break;
}
puts(tmp==mex+1?"Yes":"No");
}
else puts(mex!=n?"Yes":"No");
}
return fflush(stdout),fclose(stdin),fclose(stdout),0;
}
b
有\(n\)個陣列\(c_i\),長度均為\(m\),初始值為\(c_{i,j}=b_j\)。有兩種操作:
操作一:\(1<i<j<n\),\(a_i\leftarrow a_i-1\),\(a_j\leftarrow a_j-1\),\(a_{i-1}\leftarrow a_{i-1}+1\),\(a_{j+1}\leftarrow a_{j+1}+1\)
操作二:\(1<i<j<n-1\),\(a_i\leftarrow a_i-1\),\(a_j\leftarrow a_j-1\),\(a_{i-1}\leftarrow a_{i-1}+1\),\(a_{j+2}\leftarrow a_{j+2}+1\)
已知\(c_k\)經歷了\(x\)操作二,其他序列經歷了若干次操作一,求\(k\)和\(x\)。
題解:已知一序列設為\(0,0,0,0,0,0,0,0\)
經歷操作一後序列為\(0,1,-1,0,-1,1,0,0\)
字首和為\(0,1,0,0,-1,0,0,0\)
經歷操作一後序列為\(0,1,-1,0,-1,0,1,0\)
字首和為\(0,1,0,0,-1,-1,0,0\)
經歷操作一後字首和的和不變,經歷操作二後字首和的和減一,求\(c_i\)的字首和的和即可。
時間複雜度\(\Theta(\sum nm)\)
程式碼:
#include<cstdio>
#include<cstring>
#define int long long
const int N=1000005;
int n,m,T,v[N],sum[N];
inline int pos(int x,int y){
return (x-1)*m+y;
}
signed main(){
for(freopen("b.in","r",stdin),freopen("b.out","w",stdout),scanf("%lld",&T);T--;){
scanf("%lld%lld",&n,&m),memset(v,0,sizeof(v)),memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
scanf("%lld",v+pos(i,j));
if(j^1)v[pos(i,j)]+=v[pos(i,j-1)];
sum[i]+=v[pos(i,j)];
}
sum[n+1]=sum[1];
for(int i=1;i<=n;i++)if(sum[i]<sum[i+1]){
printf("%lld %lld\n",i,sum[i+1]-sum[i]);
break;
}
}
return fflush(stdout),fclose(stdin),fclose(stdout),0;
}
c
題面:有\(n\)個點的完全圖,每個點有一個由N
或者Y
組成的長度為\(m\)的字串,兩點之間邊權為字串不同的地方的數量,求最小生成樹。
題解:將字串轉化成二進位制表示,定義\(f(i,j)\)表示與狀態\(i\)有\(j\)個字母不同且作為點上的字串,雙向搜尋轉移狀態即可。
其中長度為奇數的距離拆成\(k\)和\(k+1\),長度為偶數的距離拆成兩個\(k\)。
最後先搜尋到的兩點之間距離使用kruskal
演算法即可。
複雜度\(O(2^m\times m)\)
程式碼:
#include<cstdio>
#include<cstring>
#define int long long
const int N=300005;
int T,n,m,v[N],fa[N],size[N],ans,f[N][40];
char s[N];
int find(int x){return x^fa[x]?fa[x]=find(fa[x]):x;}
inline void swap(int&x,int&y){x^=y^=x^=y;}
bool merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return false;
if(size[x]<size[y])swap(x,y);
fa[y]=x,size[x]+=size[y];
return true;
}
signed main(){
for(freopen("c.in","r",stdin),freopen("c.out","w",stdout),scanf("%lld",&T);T--;){
scanf("%lld%lld",&n,&m),memset(v+1,0,n<<3),ans=0,memset(f,0,sizeof(f));
for(int i=1;i<=n;i++){
scanf("%s",s+1),fa[i]=i,size[i]=1;
for(int j=1;j<=m;j++)v[i]=(v[i]<<1)|(s[j]=='Y');
f[v[i]][0]=i;
}
int cnt=n-1;
for(int i=0;i<=m/2;i++){
for(int j=0;j<1<<m;j++)if(f[j][i])for(int k=0;k<m;k++)if(f[j^(1<<k)][i]&&merge(f[j][i],f[j^(1<<k)][i]))ans+=(i<<1)+1,cnt--;
if(!cnt)break;
for(int j=0;j<1<<m;j++)if(f[j][i])for(int k=0;k<m;k++){
if(f[j^(1<<k)][i+1]){
if(merge(f[j][i],f[j^(1<<k)][i+1]))ans+=(i<<1)+2,cnt--;
}
else f[j^(1<<k)][i+1]=f[j][i];
}
if(!cnt)break;
}
printf("%lld\n",ans);
fflush(stdout);
}
return fflush(stdout),fclose(stdin),fclose(stdout),0;
}