• Codeforces Round #771 (Div. 2)思路分享


    Codeforces Round #771 (Div. 2)

    B题最后被hack了.....不过最后还是加了17。。。不掉就行,下场要上粉!!!

    A. Reverse

    题目要求最小的字典序序列,比较显然的想法就是第一个元素找1,第二个元素找2....依次类推。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long  long
    #define db double 
    using namespace std;
    int n,T,a[510],b[510]; 
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]);
            for(int i=1;i<=n;++i)
            {
                if(i!=a[i])
                {
                    int o;
                    for(int j=i+1;j<=n;++j)
                    {
                        if(i==a[j])
                        {
                            o=j;
                            break;
                        }
                    }
                    for(int j=i;j<=o;++j) b[j]=a[i+o-j];
                    for(int j=i;j<=o;++j) a[j]=b[j];
                    break;
                }
            }
            for(int i=1;i<=n;++i) printf("%d ",a[i]);
            puts("");
        }
        return 0;
    } 
    

    B. Odd Swap Sort

    比赛的时候就被这个题卡了许久,到最后还是打了个\(O(n^2)\)的复杂度上去,数据1e5,我......脑子抽抽了估计...
    发现只奇偶的才能换,也就是说相同的奇偶性是不能交换的,换句话说也就是相同奇偶性的数他们的相对位置是不变的。那么我们只需要分别对于奇数和偶数观察他们是否是有序的即可。

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int T,n;
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            bool flag=true;
            int l=0,r=0;
            for(int i=1;i<=n;++i)
            {
                int x;scanf("%d",&x);
                if(x%2==1&&x<l) flag=false;
                if(x%2==0&&x<r) flag=false;
                (x%2)?l=x:r=x; 
            }
            if(flag) puts("YES");
            else puts("NO");
        }
        return 0;
    } 
    

    C. Inversion Graph

    C题一眼看过去就是知道是优化连边的题,对于逆序对,我的第一想法就是用树状数组求逆序对的方法。就是倒序,然后按权值大小,累计,每一个x,1 - x-1内的个数就是它对应的逆序数的个数。那么在这个题中就是x要与1 - x-1内的所有数连边。暴力的连边不可行,我们考虑怎么优化,我们最终的目的是让1 - x-1与x形成一个连通块,那么我们可以保留最小的那个数,将其他数都删去,这样,能连上这个连通块的其他的数时,一定也能连上这个连通块内的最小的数字。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long  long
    #define db double 
    using namespace std;
    const int N=1e5+10;
    int n,T,a[N],f[N],vis[N],b[N],num;
    priority_queue<int>q;
    inline int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]);
            for(int i=1;i<=n;++i) f[i]=i,vis[i]=0;
            while(q.size()) q.pop();
            for(int i=n;i>=1;--i)
            {
                num=0;
                while(q.size()&&(-q.top())<a[i])
                {
                    b[++num]=-q.top();
                    q.pop();
                }
                if(num)
                {
                    for(int j=2;j<=num;++j) f[b[j]]=b[1];
                    f[a[i]]=b[1];
                    q.push(-b[1]);
                }
                else q.push(-a[i]);
            }
            int cnt=0;
            for(int i=1;i<=n;++i) 
            {
                int t=getf(i);
                if(vis[t]) continue;
                vis[t]=1;cnt++;
            }
            printf("%d\n",cnt);
        }
        return 0;
    } 
    

    这个题当然也有其他的方法,我是采用优先队列去模拟这个过程。其实我们完全可以用单调栈去模拟这个过程,我们维护一个栈底到栈顶单调递增的栈,一旦出现一个元素将其他元素顶出栈时,我们考虑保留当中最大的元素,将所有的元素与最大的元素连边即可。

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int T,n,a[N],st[N],top,f[N],vis[N];
    inline int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for(int i=1;i<=n;++i) scanf("%d",&a[i]),f[i]=i,vis[i]=0;
            top=0;
            for(int i=1;i<=n;++i)
            {
                int ts=a[i];
                while(top&&a[i]<st[top])
                {
                    ts=max(ts,st[top]);
                    f[st[top]]=ts;
                    top--;
                }
                f[a[i]]=ts;
                st[++top]=ts;
            }
            int cnt=0;
            for(int i=1;i<=n;++i)
            {
                int t=getf(i);
                if(vis[t]) continue;
                vis[t]=1;
                cnt++;
            }
            printf("%d\n",cnt);
        }
        return 0;
    } 
    

    D. Big Brush

    这个题算是最后逆风翻盘了吧...记得之前在洛谷做过一个类似的题,这种覆盖性问题的解法就是倒着解....我们先找最后一个四个一起的小矩形,之后这些小矩形可以变成任意颜色,一直扩展即可。

    #include #define ll long long #define db double using namespace std; const int N=1010; int n,m,c[N][N],num; queue >q; struct wy{int x,y,c;}b[N*N]; inline bool check(int x,int y) { if(x>=1&&x=1&&y=1;--i) printf("%d %d %d\n",b[i].x,b[i].y,b[i].c); return 0; } 点击查看代码
    #include<bits/stdc++.h>
    #define ll long  long
    #define db double 
    using namespace std;
    const int N=1010;
    int n,m,c[N][N],num;
    queue<pair<int,int> >q;
    struct wy{int x,y,c;}b[N*N];
    inline bool check(int x,int y)
    {
        if(x>=1&&x<n&&y>=1&&y<m)
        {
            int mx=max(max(c[x][y],c[x][y+1]),max(c[x+1][y],c[x+1][y+1]));
            if(!mx) return false;
            if(c[x][y]&&c[x][y]!=mx) return false; 
            if(c[x][y+1]&&c[x][y+1]!=mx) return false;
            if(c[x+1][y]&&c[x+1][y]!=mx) return false;
            if(c[x+1][y+1]&&c[x+1][y+1]!=mx) return false;
            return true;
        }
        return false;
    }
    inline void clear(int x,int y)
    {
        c[x][y]=c[x][y+1]=0;
        c[x+1][y]=c[x+1][y+1]=0;
    }
    inline bool duan()
    {
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j) if(c[i][j]) return false;
        return true;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j) 
            {
                scanf("%d",&c[i][j]);
                if(check(i,j)) q.push({i,j});
            }   
        while(q.size())
        {
            int x=q.front().first,y=q.front().second;
            q.pop();
            if(check(x,y))
            {
                b[++num].x=x;
                b[num].y=y;
                b[num].c=max(max(c[x][y],c[x][y+1]),max(c[x+1][y],c[x+1][y+1]));
                clear(x,y);
                for(int i=-1;i<=1;++i)
                    for(int j=-1;j<=1;++j) q.push({x+i,y+j});
            }    
        }    
        if(!duan()) {puts("-1");return 0;}
        printf("%d\n",num); 
        for(int i=num;i>=1;--i) printf("%d %d %d\n",b[i].x,b[i].y,b[i].c);
        return 0;
    } 
    

    E. Colorful Operations

    关于这种线段树的延迟操作,其实之前也是写过类似的题目的.....就是没想到这个题可以这样写。
    先把问题简化,如果只有单点修改的话,那我们可以新增一个变量color[],表示某个颜色总的变化量。对于操作一:我们输出a[x]+color[c[x]].对于操作二,color[x]+=v;对于操作一:我们可以a[x]+=color[c[x]];c[x]=k;a[x]-=color[c[x]];这样的话,我们全部操作即可在O(1)操作内完成。
    至于变成区间修改的话,大体的想想,因为操作1的原因,整个序列大概率是一段一段连续的数字。那我们可以维护一个区间是否颜色相同,相同的话在上面做修改。不同的话,继续往下找。虽然某个操作的复杂度可能很大,但均摊下去应该不大。.....(具体证明还没有想明白...先这样咕掉...)

    点击查看代码
    #include<bits/stdc++.h>
    #define ls p<<1
    #define rs p<<1|1
    #define ll long long
    using namespace std;
    const int N=1e6+10;
    int n,q;
    char c[100]; 
    ll color[N];
    struct Tree
    {
        int col;
        bool sa;
        ll la;    
        #define sa(p) t[p].sa
        #define la(p) t[p].la
        #define col(p) t[p].col
    }t[N<<2];
    inline void build(int p,int l,int r)
    {
        sa(p)=1;col(p)=1;
        if(l==r) return;
        int mid=l+r>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);    
    } 
    inline void push(int p)
    {
        if(la(p))
        {
            la(ls)+=la(p);
            la(rs)+=la(p);
            la(p)=0;
        }
        if(sa(p)&&col(p))
        {
            sa(ls)=1;sa(rs)=1;
            col(ls)=col(p);
            col(rs)=col(p);
        }
    }
    inline ll ask(int p,int l,int r,int x)
    {
        if(l==r) return la(p)+color[col(p)];   
        push(p);
        int mid=l+r>>1;
        if(x<=mid) return ask(ls,l,mid,x);
        else return ask(rs,mid+1,r,x);
    }
    inline void alter(int p,int l,int r,int L,int R,int c)
    {
        if(l<=L&&r>=R&&sa(p))
        {
            sa(p)=1;
            la(p)+=color[col(p)]-color[c];
            col(p)=c;
            return;
        }
        push(p);
        int mid=L+R>>1;
        if(l<=mid) alter(ls,l,r,L,mid,c);
        if(r>mid) alter(rs,l,r,mid+1,R,c);
        if(sa(ls)&&sa(rs)&&col(ls)==col(rs)) sa(p)=1,col(p)=col(ls);
        else sa(p)=0;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
        scanf("%d%d",&n,&q);
        build(1,1,n);
        for(int i=1;i<=q;++i)
        {
            scanf("%s",c+1);
            if(c[1]=='C') 
            {
                int l,r,c;
                scanf("%d%d%d",&l,&r,&c);
                alter(1,l,r,1,n,c);
            }
            else if(c[1]=='A') 
            {
                int c,x;
                scanf("%d%d",&c,&x);
                color[c]+=x;
            }
            else 
            {
                int x;scanf("%d",&x);
                printf("%lld\n",ask(1,1,n,x));
            }
        }
        return 0;
    }
    

    咕咕咕,我来补证明了....观察我们的做法发现就操作1是影响整个题的复杂度的。考虑我们每次操作一会修改多少个节点?加入当前这个区间内有k个颜色块(每个颜色块内的颜色相同),则我们至多会做k次区间修改(正常的).所以我们发现我们操作一的复杂度是和这个序列颜色块的个数相关的。最初这个序列的颜色块个数为p=1,之后我们每次修改都将某个区间的颜色修改成一个颜色,那么对于整个序列的颜色块而言,可能出现的情况为p+=2,修改后的区间边缘与两边都不相同,增加两个颜色块.还可能出现p-=(k-1)由最初的k个颜色块变成1个颜色块。那我们操作一总的复杂度就是每次操作后整个序列的p值的累计。既然这样,我们可以继续抽象,给定一个数字1,每次最多加2,或者减去任意值,问这个数字的不断累计后的值。不断累计的值其实就是1加上总的变化值,考虑总的变化值,每次加2,加入我们只加最后的结果还是2n,复杂度还是O(n)级别的。所以相当于我们最后只做了n次区间修改,复杂读完全可以。

  • 相关阅读:
    ScrollVIew 边界阴影效果
    android 开发 常用工具类
    alertdialog.builder 自定义弹窗
    垂直的seekbar
    SlideSwitch
    [转]Android_气泡效果
    ListView 滚回顶部
    04_dljd_mysql登录和退出
    03_dljd_启动和关闭mysql服务
    02_dljd_mysql数据库的下载、安装与卸载
  • 原文地址:https://www.cnblogs.com/gcfer/p/15915481.html
Copyright © 2020-2023  润新知