Codeforces Round #541 (Div. 2)題解

不知道該更些什麼ios

隨便寫點東西吧git

https://codeforces.com/contest/1131spa


ABC

太熱了不寫了code

D

把相等的用並查集縮在一塊兒blog

若是$ x<y$則從$ x$往$y$連邊字符串

若是並查集內有邊或邊構成環則無解get

不然作一遍拓撲輸出答案string

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x = 0; char zf = 1; char ch = getchar();
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int i,j,k,m,n,x,y,z,cnt,sum;
char c[1010][1010];int fa[2010];
int ask(int x){
    return (x==fa[x])?x:(fa[x]=ask(fa[x]));
}
void unite(int x,int y){
    int p=ask(x),q=ask(y);
    if(p!=q)fa[q]=p;
}
struct query{
    int x,y;
}q[2000010];int t;
int F[2000010],L[2000010],N[2000010],a[2000010],du[2000010],ans[2000010];
void add(int x,int y){
    a[++k]=y;du[y]++;
    if(!F[x])F[x]=k;
    else N[L[x]]=k;
    L[x]=k;
}
int qu[2000010],h;bool vis[2000010];
int main(){
    n=read();m=read();
    for(rt i=1;i<=n+m;i++)fa[i]=i;
    for(rt i=1;i<=n;i++){
        scanf("%s",c[i]+1);
    }
    for(rt i=1;i<=n;i++)
    for(rt j=1;j<=m;j++)if(c[i][j]=='=')unite(i,j+n);
    else if(c[i][j]=='<')q[++t]={i,j+n};
    else q[++t]={j+n,i};
    for(rt i=1;i<=t;i++){
        int x=ask(q[i].x),y=ask(q[i].y);
        if(x==y){
            puts("No");
            return 0;
        }
        add(x,y);
    }
    h=t=0;
    for(rt i=1;i<=n+m;i++)if(!du[i]&&i==ask(i))qu[++t]=i,ans[i]=1;
    while(h<t){
        x=qu[++h];vis[x]=1;
        for(rt i=F[x];i;i=N[i]){
            ans[a[i]]=max(ans[a[i]],ans[x]+1);
            if(!--du[a[i]])qu[++t]=a[i];
        }
    }
    for(rt i=1;i<=n+m;i++)if(i==ask(i)&&du[i]){
        puts("No");return 0;
    }
    puts("Yes");
    for(rt i=1;i<=n;i++)write(ans[ask(i)]),putchar(' ');putchar('\n');
    for(rt i=n+1;i<=n+m;i++)write(ans[ask(i)]),putchar(' ');
    return 0;
}

E

寫法不少,關鍵是要找到一種好寫的寫法it

對每一個字符串的每一個字符求出其先後綴長度和最長連續子串的長度io

$ f_{i,j}$表示前$ i$個字符串的積字符$ j$的最長連續子串長度

對下一個要乘的字符串進行討論(是否所有相同)

注意細節就行了

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x=0;char zf=1;char ch=getchar();
    while(ch!='-'&&!isdigit(ch))ch=getchar();
    if(ch=='-')zf=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int k,m,n,x,y,z,cnt,ans,len;
char c[100010];
int f[100010][30];//最長子串
int pre[30],succ[30],mx[30]; 
void chk(){
    for(rt i='a';i<='z';i++){
        for(rt j=1;j<=len+1;j++)if(c[j]!=i||j>len){
            pre[i-'a']=j-1;
            break;
        }
        for(rt j=len;j>=0;j--)if(c[j]!=i||j<=0){
            succ[i-'a']=len-j;
            break;
        }
        int now=0;mx[i-'a']=0;
        for(rt j=1;j<=len;j++){
            if(c[j]==i)now++;
            if(j==len||c[j]!=i){
                mx[i-'a']=max(mx[i-'a'],now);
                now=0;
            }
        }
    }
}
int main(){
    n=read();
    for(rt i=1;i<=n;i++){
        scanf("%s",c+1);len=strlen(c+1);chk();
        if(i==1)for(rt j='a';j<='z';j++)f[1][j-'a']=mx[j-'a'];
        else {
            for(rt j='a';j<='z';j++)if(f[i-1][j-'a'])f[i][j-'a']=1;
            for(rt j='a';j<='z';j++)if(f[i-1][j-'a']){
                if(pre[j-'a']==len)
                f[i][j-'a']=(f[i-1][j-'a']+1)*len+f[i-1][j-'a'];
                else f[i][j-'a']=1+pre[j-'a']+succ[j-'a'];
            }
        }
        for(rt j='a';j<='z';j++)f[i][j-'a']=max(f[i][j-'a'],mx[j-'a']);
    }
    int ans=0;
    for(rt i='a';i<='z';i++)ans=max(ans,f[n][i-'a']);
    cout<<ans;
    return 0;
}

F

大送分題

按題意模擬,每次合併$x,y$的時候新建一個新點連向$ x,y$所對應的根

而後一遍$ dfs$結束

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rt register int
#define ll long long
#define p 998244353
using namespace std;
inline ll read(){
    ll x = 0; char zf = 1; char ch = getchar();
    while (ch != '-' && !isdigit(ch)) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int i,j,k,m,n,x,y,z,cnt,sum;
int F[450010],N[450010],L[450010],a[450010],fa[450010];
void add(int x,int y){
    a[++k]=y;fa[y]=x;
    if(!F[x])F[x]=k;
    else N[L[x]]=k;
    L[x]=k;
}
int ask(int x){
    return (x==fa[x])?x:(fa[x]=ask(fa[x]));
}
void dfs(int x){
    if(x<=n)write(x),putchar(' ');
    for(rt i=F[x];i;i=N[i])dfs(a[i]);
}
int main(){
    n=read();
    for(rt i=1;i<=n;i++)fa[i]=i;
    cnt=n;
    for(rt i=1;i<n;i++){
        x=read();y=read();
        cnt++;fa[cnt]=cnt;
        int id1=ask(x),id2=ask(y); 
        add(cnt,id1);add(cnt,id2);
    }
    dfs(cnt);
    return 0;
}

G

小清新DP題

(有好多人帶$\log$都過了

先對每一個多米諾骨牌$ i$求出$ L_i$和$ R_i$表示往左/右最近的不能推倒的位置

用一個單調棧維護

即若是當前棧頂可以被這個多米諾骨牌推倒則必定不可能成爲後面的多米諾骨牌的L/R值

求出L/R以後進行DP

設$ f_i$表示推倒了前$ i$個的最小花費

分兩種轉移

若是當前多米諾骨牌往左倒則$ f_i=f_{L_i}+cost_i$

這種顯然能夠$ O(1)$轉移

若是是以前的某個多米諾骨牌往右倒則$f_i=\min(f_j+cost_{j+1})(R_j>i)$

用一個單調棧維護

容易發現因爲題目的優秀性質,若是三塊多米諾骨牌$ k<j<i$知足$ k和j$都能倒到$ i$

則$ R_k \geq R_j$

所以全部可以倒到當前多米諾骨牌的牌必定知足原位置越靠左R值反而越大

用一個單調棧維護R值,每次把棧中全部不能到達當前多米諾骨牌的R值彈出取棧頂轉移

維護一個離棧頂越近,轉移費用$ f_j+cost_{j+1}$越小,R值越大的單調棧

就能夠$ O(1)$轉移了

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#define rt register int
#define ll long long
using namespace std;
inline ll read(){
    ll x=0;char zf=1;char ch=getchar();
    while(ch!='-'&&!isdigit(ch))ch=getchar();
    if(ch=='-')zf=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf;
}
void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
void writeln(const ll y){write(y);putchar('\n');}
int m,n,x,y,z,cnt,ans;
vector<int>a[250010],b[250010];
int k[250010],h[10000010],t;ll c[10000010];
int L[10000010],R[10000010];
int sta[10000010],top;ll dp[10000010],fy[10000010];
int main(){
    m=read();n=read();
    for(rt i=1;i<=m;i++){
        k[i]=read();
        a[i].resize(k[i]);b[i].resize(k[i]);
        for(auto &x:a[i])x=read();
        for(auto &x:b[i])x=read();
    }
    for(rt T=read();T;T--){
        int id=read(),ct=read();
        for(rt i=0;i<k[id];i++)t++,h[t]=a[id][i],c[t]=1ll*b[id][i]*ct;
    }
    for(rt i=1;i<=n;i++)L[i]=R[i]=i;
    sta[top=0]=0;
    for(rt i=1;i<=n;i++){
        while(i-sta[top]<h[i]&&top)top--;
        L[i]=sta[top];sta[++top]=i;
    } 
    sta[top=0]=n+1;
    for(rt i=n+1;i>=1;i--){
        while(sta[top]-i<h[i]&&top)top--;
        R[i]=sta[top];sta[++top]=i;
    }
    dp[0]=0;
    sta[top=0]=0;
    for(rt i=1;i<=n;i++){
        dp[i]=dp[L[i]]+c[i];
        while(R[sta[top]]<=i&&top)top--;
        if(top)dp[i]=min(dp[i],fy[sta[top]]);fy[i]=dp[i-1]+c[i];
        while(top&&R[sta[top]]==R[i]&&fy[i]<=fy[sta[top]])top--;
        if(top==0||fy[i]<fy[sta[top]])sta[++top]=i;
    }
    cout<<dp[n];
    return 0;
}
相關文章
相關標籤/搜索