Codeforces Round #541 (Div. 2)题解

喜你入骨 提交于 2020-02-28 15:41:59

不知道该更些什么

随便写点东西吧

https://codeforces.com/contest/1131


ABC

太热了不写了

D

把相等的用并查集缩在一起

如果$ x<y$则从$ x$往$y$连边

如果并查集内有边或边构成环则无解

否则做一遍拓扑输出答案

#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

写法很多,关键是要找到一种好写的写法

对每个字符串的每个字符求出其前后缀长度和最长连续子串的长度

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

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!