zoukankan      html  css  js  c++  java
  • Codeforces Round #541 (Div. 2)题解

    不知道该更些什么

    随便写点东西吧

    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;
    }
  • 相关阅读:
    [荐]推荐一个shell学习的网站
    [转]linux远程登入不需要密码
    [转] eclipce使用vim 开启装逼模式
    Linux 下查找指令
    nmon 工具的使用
    LaTeX 符号大全
    vim 粘贴复制操作
    linux命令模式下如何切换首行和尾行
    fish 与oh-my-fish 的安装
    vim 粘贴复制操作
  • 原文地址:https://www.cnblogs.com/kananix/p/10433403.html
Copyright © 2011-2022 走看看