zoukankan      html  css  js  c++  java
  • 2020 2.1 lxs Contest #113

    https://file.floj.tech/export/eoAlAVCaKW099bc9d509

    t1

    【题解】

    题目求亮着的灯有几个连续段。

    答案可以转化为亮着的灯个数减去相邻的亮着的灯的对数。

    考虑如何求相邻亮着的灯的对数。

    求出每种颜色相邻哪几种颜色并连边,权值为这两种颜色相邻对数。

    则答案就是总点数减去亮着的点的图的边权和。

    由于某些颜色出度较多,直接枚举要T。

    所以对于所有出度小于sqrt n的点暴力枚举修改并记录他们与出度大的点造成的贡献,在该出度大的点亮时在加上该贡献。

    对于出度大的点只枚举他与其他出度大的点的边即可。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    int n,m,q,a[N],dahu[N],kuaida,siz[N],jia[N];
    struct pigu
    {
        int q,dao;
        pigu(int x=0,int y=0)
        {
            dao=x;q=y;
        }
        friend bool operator <(pigu x,pigu y)
        {
            return x.dao<y.dao;
        }
    };
    vector <pigu> ma[N],hu,ma2[N];
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    bool book[N],xiao[N];
    int main()
    {
    //    freopen("light.in","r",stdin);
    //    freopen("light.out","w",stdout);
        n=read();m=read();q=read();kuaida=sqrt(n);
        for(int i=1;i<=n;i++) a[i]=read();
        int tot=0;
        for(int i=1;i<=n;i++) if(a[i]!=a[i-1]) dahu[++tot]=a[i];
        for(int i=1;i<=tot;i++) a[i]=dahu[i],siz[a[i]]++;
        n=tot;
        for(int i=2;i<=tot;i++) ma[a[i]].push_back(pigu(a[i-1],1)),ma[a[i-1]].push_back(pigu(a[i],1));
        for(int i=1;i<=m;i++)
        {
            int len=ma[i].size();
            if(!len) continue;
            sort(ma[i].begin(),ma[i].end());
            int ge=0;
            hu.clear();
            hu.push_back(pigu(ma[i][0].dao,1));
            for(int j=1;j<len;j++)
            {
                if(ma[i][j].dao==ma[i][j-1].dao) hu[ge].q++;
                else ge++,hu.push_back(pigu(ma[i][j].dao,1));
            }
            ma[i].clear();
            for(int j=0;j<=ge;j++) ma[i].push_back(hu[j]);
        }
        for(int i=1;i<=m;i++)
        {
            int len=ma[i].size();
            if(len<kuaida) xiao[i]=1;
        }
        for(int i=1;i<=m;i++)
            if(xiao[i]==0)
            {
                int len=ma[i].size();
                for(int j=0;j<len;j++)
                    if(xiao[ma[i][j].dao]==0)
                        ma2[i].push_back(ma[i][j]);
            }
        int ans=0,jian=0;
        while(q--)
        {
            int x=read();
            int pan=book[x]?-1:1;
            book[x]^=1;
            ans+=siz[x]*pan;
            if(xiao[x]==1)
            {
                int len=ma[x].size();
                for(int i=0;i<len;i++)
                {
                    if(xiao[ma[x][i].dao]==0) jia[ma[x][i].dao]+=ma[x][i].q*pan;
                    if(book[ma[x][i].dao]==0) continue;
                    jian+=ma[x][i].q*pan;
                }
            }
            else
            {
                int len=ma2[x].size();
                for(int i=0;i<len;i++)
                {
                    if(book[ma2[x][i].dao]==0) continue;
                    jian+=pan*ma2[x][i].q;
                }
                jian+=pan*jia[x];
            }
            cout<<ans-jian<<"
    ";
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

    t2

    【题解】

    可以对于每个相邻的不为0的点连边边权为他们的值的差。发现如果形成了环,这环上权值和必定为周期的倍数。

    所以就dfs找环,找到就求gcd即可。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,now[100005],dis[100005],last[100005],size;
    struct pigu
    {
        int dao,ne,quan;
    }a[200005];
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    inline void lingjiebiao(int x,int y,int z)
    {
        a[++size].dao=y;
        a[size].quan=z;
        a[size].ne=last[x];
        last[x]=size;
    }
    inline int gcd(int x,int y)
    {
        if(x<0) x=-x;if(y<0) y=-y;
        if(x==0||y==0) return x|y;
        return x%y==0?y:gcd(y,x%y);
    }
    int ans=0;
    bool book[100005];
    inline void dfs(int no,int d)
    {
        if(book[no])
        {
            ans=gcd(ans,d-dis[no]);
            return;
        }
        book[no]=1;
        dis[no]=d;
        for(int i=last[no];i;i=a[i].ne)
        {
            dfs(a[i].dao,d+a[i].quan);
        }
    }
    int main()
    {
    //    freopen("crossing.in","r",stdin);
    //    freopen("crossing.out","w",stdout);
        n=read();m=read();
        for(int i=1;i<=m;i++)
        {
            int sh=0;
            for(int j=1;j<=n;j++)
            {
                now[j]=read();
                if(now[j]==0) continue;
                if(sh) lingjiebiao(sh,j,now[j]-now[sh]),lingjiebiao(j,sh,now[sh]-now[j]);
                sh=j;
            }
        }
        for(int i=1;i<=n;i++) if(!book[i]) dfs(i,0);
        if(!ans) cout<<"-1";
        else cout<<ans;
        fclose(stdin);fclose(stdout);
        return 0;
    }
    /*
    4 5
    0 33 0 36
    0 4 0 7
    42 2 42 5
    0 21 0 24
    8 0 8 54
    */
    View Code

    t3

    【题解】

    发现到了最后图就分成很多块,每块中都有一部分人可以在自己块中乱走,但人数不够走到前头。

    定义f[i][j]表示在第i个密室内有j个人时,前头最多有好多个人。在转移时必须保证右边的人不能走到左边来,都是左边人走到右边或者右边有一些人但走不过来,否则就会有m个人在1中。

    注意下文=都是表示取max。

    当j<a[i]时,在下一间有b[i]个人时,这些人可以走过去,f[i+1][b[i]+j]=f[i][j]+b[i]。

    当j<a[i]时,设mx=max(f[i][j]),则下一间本身有<b[i]个人时,f[i+1][j]=mx+j。

    当a[i]≤j<a[i]+b[i]时,可以走j-a[i]个人到下一间,f[i+1][j-a[i]]=f[i][j]。

    当j>=a[i]+b[i]时,所有人都可以到下一间,f[i+1][j]=f[i][j]。

    还有一个性质就是任意一间密室的人不能多于前面最大的a[i]+b[i],否则人全部跑了。这就保证了j<=20000。可以过。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1005;
    int f[N][20005],da,n,m,a[N],b[N];
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    int main()
    {
        //freopen("escape.in","r",stdin);
        //freopen("escape.out","w",stdout);
        n=read();m=read();
        int ans=0;
        for(int i=1;i<=n-1;i++)
            a[i]=read(),b[i]=read();
        da=m-1;
        for(int i=0;i<=m-1;i++) f[1][i]=i;
        for(int i=1;i<=n-1;i++)
        {
            int yj=0;
            for(int j=0;j<a[i];j++) f[i+1][b[i]+j]=max(f[i+1][b[i]+j],f[i][j]+b[i]),yj=max(yj,f[i][j]);
            for(int j=0;j<b[i];j++) f[i+1][j]=max(f[i+1][j],yj+j);
            for(int j=a[i];j<a[i]+b[i];j++) f[i+1][j-a[i]]=max(f[i+1][j-a[i]],f[i][j]);
            for(int j=a[i]+b[i];j<=da;j++) f[i+1][j]=max(f[i+1][j],f[i][j]);
            da=max(da,a[i]+b[i]-1);
        }
        ans=0;
        for(int i=0;i<=da;i++) ans=max(ans,f[n][i]);
        cout<<ans;
        return 0;
    }
    View Code
  • 相关阅读:
    【题解】警位安排( 树形 DP)
    【题解】任务分配
    Leo 搭积木
    受牛仰慕的牛
    zy 送画
    [NOIP2010] 乌龟棋
    [WikiOi 1214] 线段覆盖
    【拓扑习题】课程安排问题
    [WikiOI "天梯"1281] Xn数列
    [百度之星2014资格赛] Disk Schedule 报告
  • 原文地址:https://www.cnblogs.com/betablewaloot/p/12250393.html
Copyright © 2011-2022 走看看