20240726題解

junjunccc發表於2024-07-26

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