1. 程式人生 > 實用技巧 >Goodbye 2020題解(A-G)

Goodbye 2020題解(A-G)

CF1466A:

列舉兩點,統計不同長度即可。

CF1466B:

從小到大,對於每一個數,如果它小於或等於上一個數,將其加$1$。如果大於等於上一個數則答案加1。

因為輸入已經排好序,所以正確性顯然。

程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int T,n,t,lst,ans;

int main(void)
{
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        lst
=-1; ans=0; while(n--){ scanf("%d",&t); if(t==lst) t++; if(t>lst) ans++; if(lst<t) lst=t; } printf("%d\n",ans); } return 0; }
View Code

CF1466C:

等價於沒有長度為$2$或$3$的迴文串,即不存在$i$使$s_{i-1}=s_i$或者$s_{i-1}=s_{i+1}$。

所以如果有兩個相同的字元隔得足夠近,必須改其中至少一個。

又因為字符集夠大,所以由上述條件建成的圖(兩個必須改一個則1連邊)中最小點覆蓋就是答案。

因為只有相鄰點有連邊,所以考慮$dp$。

$dp_{0/1,0/1,i}$表示前$i$個字元,第$i-1$個字元不改/改,第$i$個字元不改/改的最小代價。

強行分類,然後轉移。

程式碼很噁心:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int T,n;
char s[100005];
int dp[2][2][100005];

int main(void)
{
    scanf(
"%d",&T); while(T--){ scanf(" %s",s); n=strlen(s); if(n==1){printf("0\n");continue;} dp[0][0][1]=0; dp[0][1][1]=1; dp[1][0][1]=1; dp[1][1][1]=2; if(s[0]==s[1]) dp[0][0][1]=1e9; for(int i=2;i<n;i++){ dp[0][1][i]=min(dp[0][0][i-1],dp[1][0][i-1])+1; dp[1][1][i]=min(dp[0][1][i-1],dp[1][1][i-1])+1; if(s[i]==s[i-1]&&s[i]==s[i-2]){ dp[0][0][i]=1e9; dp[1][0][i]=dp[1][1][i-1]; } if(s[i]==s[i-1]&&s[i]!=s[i-2]){ dp[0][0][i]=1e9; dp[1][0][i]=min(dp[1][1][i-1],dp[0][1][i-1]); } if(s[i]!=s[i-1]&&s[i]==s[i-2]) if(i>2&&s[i-1]==s[i-3]){ dp[0][0][i]=dp[1][1][i-2]; dp[1][0][i]=dp[1][1][i-1]; } else{ dp[0][0][i]=dp[1][0][i-1]; dp[1][0][i]=dp[1][1][i-1]; } if(s[i]!=s[i-1]&&s[i]!=s[i-2]){ dp[0][0][i]=min(dp[0][0][i-1],dp[1][0][i-1]); dp[1][0][i]=min(dp[0][1][i-1],dp[1][1][i-1]); } } int ans=min(dp[0][0][n-1],dp[0][1][n-1]); ans=min(ans,min(dp[1][0][n-1],dp[1][1][n-1])); printf("%d\n",ans); } return 0; }
View Code

CF1466D:

每一種顏色只取最大權值和的連通塊,所以把其它連通塊換顏色答案不會更劣。

於是每種顏色只有一個連通塊。

假設有$k$種顏色,那麼這$k$個連通塊共有$k-1$個交點。

總權值就是所有點權乘加上該點被包含次數之和。

所有點被包含次數和明顯為$n+k-1$(每加入一種顏色次數和加一),每個點最多被包含的次數是其度數。

於是把點權放在一起排序並求字首和即可。

程式碼很簡單:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long int

int T,n,a,b; ll ans;
int deg[100000],w[100000];
int val[200000],cnt;

int main(void)
{
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        cnt=0; ans=0;
        for(int i=0;i<n;i++){
            scanf("%d",&w[i]);
            deg[i]=0; ans+=w[i];
        }
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            a--; b--;
            if(deg[a]>0) val[cnt++]=w[a];
            if(deg[b]>0) val[cnt++]=w[b];
            deg[a]++; deg[b]++;
        }
        sort(val,val+cnt);
        for(int i=cnt-1;i>=0;i--){
            printf("%lld ",ans);
            ans+=val[i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

CF1466E:

$$\sum_i^n \sum_j^n \sum_k^n(x_i \& x_j)(x_j \| x_k)$$

$$= \sum_j^n ( \sum_i^n x_i \& x_j)( \sum_k^n x_j \| x_k)$$

對於每一位,計算在全部$x_i$種這一位$1$的個數即可計算左右兩邊的值。

程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long int
#define mod 1000000007
inline int sum(int a,int b){return a+b<mod?a+b:a+b-mod;}
inline int mul(int a,int b){return (int)((ll)a*b%mod);}

int T,n; ll a[500000];
int p1[500000],p2[500000];

int main(void)
{
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%lld",&a[i]);
            p1[i]=p2[i]=0;
        }
        int w=1;
        for(int i=0;i<60;i++){
            int f0=0,f1=0;
            for(int j=0;j<n;j++)
                if((a[j]>>(ll)i)&1ll) f1++;
                else f0++;
            for(int j=0;j<n;j++)
                if((a[j]>>(ll)i)&1ll){
                    p1[j]=sum(p1[j],mul(w,f1));
                    p2[j]=sum(p2[j],mul(w,n));
                }
                else p2[j]=sum(p2[j],mul(w,f1));
            w=sum(w,w);
        }
        int ans=0;
        for(int i=0;i<n;i++)
            ans=sum(ans,mul(p1[i],p2[i]));
        printf("%d\n",ans);
    }
    return 0;
}
View Code

CF1466F:

兩個位置是$1$則在這兩個位置連邊,一個位置打標記,這樣一個連通塊中:

如果有標記則這些位置中的任意狀態均可以達到。

如果沒有標記則在這些位置中,$1$的個數為偶數的狀態都可以達到。

證明?有標記的話所有涉及兩個位置的操作都可以轉化成一個位置的操作。

無標記就找生成樹,一個狀態就是一些需要改變的點,於是從葉子到根依次執行該點和其父親的邊所對應操作即可。

並查集維護連通塊可以找最小字典序的解。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long int
#define mod 1000000007
inline int mul(int a,int b){return (int)((ll)a*b%mod);}

int n,m,ans=1;
int fa[500000];
bool have[500000];
int use[500000],cnt=0;
int find(int x)
{
    if(x==fa[x]) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}

int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++) fa[i]=i;
    for(int i=1;i<=n;i++){
        int k,a,b;
        scanf("%d%d",&k,&a);
        if(k==2) scanf("%d",&b);
        a--; b--;
        if(k==1){
            a=find(a);
            if(!have[a]) use[cnt++]=i;
            if(!have[a]) ans=mul(ans,2);
            have[a]=1;
        }
        if(k==2){
            a=find(a); b=find(b);
            if(a==b||(have[a]&&have[b]))
                continue;
            fa[a]=b; have[b]|=have[a];
            use[cnt++]=i;
            ans=mul(ans,2);
        }
    }
    printf("%d %d\n%d",ans,cnt,use[0]);
    for(int i=1;i<cnt;i++)
        printf(" %d",use[i]);
    putchar('\n');
    return 0;
}
View Code

CF1466G:

將$s_i$變成$s_{i+1}=s_it_is_i$看作一次展開操作。

對於單個查詢:

暴力展開原字串直到長度大於查詢串長度。

分兩種情況:

第一種是展開後字串的字串,這部分未來每次展開操作後出現次數翻倍。

剩下的下午寫。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long int
#define mod 1000000007
inline int sum(int a,int b){return a+b<mod?a+b:a+b-mod;}
inline int mul(int a,int b){return (int)((ll)a*b%mod);}
inline int pow(int a,int b)
{
    int res=1;
    while(b>0){
        if(b&1) res=mul(res,a);
        a=mul(a,a); b>>=1;
    }
    return res;
}
inline int inv(int x){return pow(x,mod-2);}
#define mod2 998244353
inline int sum2(int a,int b){return a+b<mod2?a+b:a+b-mod2;}
inline int mul2(int a,int b){return (int)((ll)a*b%mod2);}

const int base=29;
int n,m=0,M=0,q;
char s[4000005];
char trans[100005];
int L[1000],R[1000];
int hsh1[4000005];
int hsh2[4000005];
int pow1[2000005];
int pow2[2000005];
char qu[1000005];
int hshq1[1000005];
int hshq2[1000005];
int ql[100000];
int qr[100000];
int qk[100000];
int sumv[26][100005];
int pow02[100005];
int ipow2[100005];
int res[26];

void read(void)
{
    scanf("%d%d",&n,&q);
    scanf(" %s %s",s,trans);
    L[0]=0; R[0]=strlen(s)-1;
    int lst=-1;
    for(int i=0;i<q;i++){
        ql[i]=lst+1;
        scanf("%d %s",&qk[i],qu+ql[i]);
        qr[i]=lst=ql[i]+strlen(qu+ql[i])-1;
        M=max(M,qr[i]-ql[i]+1);
    }
    while(m<n&&R[m]-L[m]+1<M){
        int len=R[m]-L[m]+1;
        m++; L[m]=R[m-1]+1;
        R[m]=L[m]+len+len;
        s[L[m]+len]=trans[m-1];
        for(int i=0;i<len;i++)
            s[L[m]+i]=s[L[m]+len+1+i]=s[L[m-1]+i];
    }
    return;
}

void gethsh(void)
{
    pow1[0]=pow2[0]=1;
    for(int i=1;i<=M+M;i++){
        pow1[i]=mul(pow1[i-1],base);
        pow2[i]=mul2(pow2[i-1],base);
    }
    for(int i=0;i<=m;i++){
        hsh1[L[i]]=hsh2[L[i]]=s[L[i]]-'a';
        for(int j=L[i]+1;j<=R[i];j++){
            hsh1[j]=sum(mul(hsh1[j-1],base),s[j]-'a');
            hsh2[j]=sum2(mul2(hsh2[j-1],base),s[j]-'a');
        }
    }
    for(int i=0;i<q;i++){
        hshq1[ql[i]]=hshq2[ql[i]]=qu[ql[i]]-'a';
        for(int j=ql[i]+1;j<=qr[i];j++){
            hshq1[j]=sum(mul(hshq1[j-1],base),qu[j]-'a');
            hshq2[j]=sum2(mul2(hshq2[j-1],base),qu[j]-'a');
        }
    }
    return;
}
inline bool check(int i,int l1,int r1,int j,int l2,int r2)
{
    if(l1>r1) return 1;
    int h1=hsh1[r1],h2=hshq1[r2],len=r1-l1+1;
    if(L[i]!=l1) h1=sum(h1,mod-mul(hsh1[l1-1],pow1[len]));
    if(ql[j]!=l2) h2=sum(h2,mod-mul(hshq1[l2-1],pow1[len]));
    if(h1!=h2) return 0;
    h1=hsh2[r1]; h2=hshq2[r2];
    if(L[i]!=l1) h1=sum2(h1,mod2-mul2(hsh2[l1-1],pow2[len]));
    if(ql[j]!=l2) h2=sum2(h2,mod2-mul2(hshq2[l2-1],pow2[len]));
    if(h1!=h2) return 0;
    return 1;
}

int main(void)
{
    read(); gethsh();
    pow02[0]=ipow2[0]=1;
    for(int i=1;i<=n;i++){
        pow02[i]=sum(pow02[i-1],pow02[i-1]);
        ipow2[i]=inv(pow02[i]);
    }
    for(int i=n-1;i>=0;i--){
        for(int j=0;j<26;j++)
            sumv[j][i]=sumv[j][i+1];
        int c=trans[i]-'a';
        sumv[c][i]=sum(sumv[c][i],pow02[n-i]);
    }
    for(int i=0;i<q;i++){
        int pos=0;
        while(pos<n&&R[pos]-L[pos]<qr[i]-ql[i]) pos++;
        if(pos>qk[i]){printf("0\n");continue;}
        int ans=0;
        for(int j=L[pos];j+qr[i]-ql[i]<=R[pos];j++)
            if(check(pos,j,j+qr[i]-ql[i],i,ql[i],qr[i]))
                ans=sum(ans,pow02[qk[i]-pos]);
        for(int j=ql[i];j<=qr[i];j++){
            bool f1=check(pos,R[pos]+ql[i]-j+1,R[pos],i,ql[i],j-1);
            bool f2=check(pos,L[pos],L[pos]+qr[i]-j-1,i,j+1,qr[i]);
            if(f1&&f2) res[qu[j]-'a']++;
        }
        for(int c=0;c<26;c++){
            res[c]=mul(res[c],mod+sumv[c][pos]-sumv[c][qk[i]]);
            ans=sum(ans,mul(res[c],ipow2[n-qk[i]+1]));
            res[c]=0;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

CF1466H:

不會

CF1466I:

不會