• 19.11.16ACM集训补题


    CodeForces 1252A

    题意是找到一个1~N的排列B,使得排列A和排列B的每个数的差的绝对值大于等于N

    原本想的是将原排列逆序输出应该可以满足要求

    然后就WA在第六个点。后来想想132这个排列的逆序231,dif只有2<3

    然后第二个思路就是将较大的数和较小的数依次交换,最后过了

    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    const int maxn=1e5+7;
    int a[maxn],n,m,b[maxn];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&m);
            a[m]=i;
        }
        for(int i=1;i<=n;i++)
            b[a[n+1-i]]=i;
        for(int i=1;i<n;i++)
            printf("%d ",b[i]);
        printf("%d
    ",b[n]);
        return 0;
    }
    View Code

    CodeForces 1252C

    题意是给出R,C两个数组,组成得到NxN的二维数组,问从起点到终点有没有每个点都是偶数的路径

    先通过R,C数组得到二维数组,然后每次判断就将起点周围的偶数区域染色,判断终点在不在这个区域,这样就可以解了

    但是N最大1e5,二维数组就超内存了,结果也没有想到好的办法,只有看题解


    让我们把点(i,j)标为0或1,分别代表这个点是偶数或奇数,同理R,C数组

    当我们看一个点从(i,j)到(i,j+1),R(i)的值不变,说明C(j)和C(j+1)同1或同0

    同理,从(i,j)到(i+1,j)说明R(i)和R(i+1)同1或同0

    而如果路径不是笔直的,例如左-下-左,我们也可以判断

    C(j)和C(j+1)同1或同0,C(j+1)和C(j+2)同1或同0,则C(j)和C(j+2)同1或同0

     所以若从(a,b)到(A,B),R(a,A)和C(b,B)两个区间都同1或同0

    当然这是有路径的必要条件

    由于起点必是偶数,则两个区间只能同1或同0,否则认为起点是奇数

    所以仅用判断起点终点是偶数,以及两个区间同1或同0,,即可证有路径

    这里用前缀和判断区间是否同0或同1

    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    const int maxn=1e5+7;
    int n,q,c[maxn],r[maxn],csum[maxn],rsum[maxn];
    int ra,ca,rb,cb;
    int main(){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
            scanf("%d",&r[i]);
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        for(int i=1;i<=n;i++)
            if(r[i]%2==r[i-1]%2)rsum[i]=rsum[i-1];
            else rsum[i]=rsum[i-1]+1;
        for(int i=1;i<=n;i++)
            if(c[i]%2==c[i-1]%2)csum[i]=csum[i-1];
            else csum[i]=csum[i-1]+1;
        while(q--){
            scanf("%d%d%d%d",&ra,&ca,&rb,&cb);
            if((r[ra]+c[ca])%2||(r[rb]+c[cb])%2){
                printf("NO
    ");
                continue;
            }
            if(ra<rb)swap(ra,rb);
            if(ca<cb)swap(ca,cb);
            if(rsum[ra]-rsum[rb]==0&&csum[ca]-csum[cb]==0){
                printf("YES
    ");
            }
            else printf("NO
    ");
        }
        return 0;
    }
    View Code

    CodeForces 1252H

    题意是给出N个矩形,找最大面积

    一种找法是矩形面积的一半

    一种找法是找到两个矩形,都满足长度大于等于l,宽度大于等于d,最大面积是max l*max d

    看到这道题,我第一反应是二分,确定一个面积后,找这个面积能不能得到,当然浮点数也可以二分

    但是这样依然想不出怎么完成第二种找法,最后。。。看题解


    第一种找法简单,在输入的时候就可以先找出来

    然后第二种找法,分别用排序和求最大值满足了l,d的条件

    众所周知,一次遍历的复杂度是O(n)(一维)

    如果能事先处理好,然后一次遍历得到答案,我觉得这个程序是写得很漂亮的

    我们想让这些矩阵由宽度d从大到小排序,后面的矩阵之前一定有宽度比他打的矩阵

    再用一个最大值记录到第i个矩阵时,他之前矩阵的最大长度,如果他长度比maxl大,那么他和maxl对应的矩阵长度都大于等于maxl,反之他和maxl对应的矩阵长度都大于等于他的长度

    这样就既满足了l和d的条件

    到这里其实我还有一个疑问,就是看答案代码时,发现在排序前将矩阵较长的一面作为排序的宽度,但不知道为什么

    一种想法是将每个输入,长宽颠倒作为两个矩阵读入,但是这样可能会出现满足条件的两个矩阵是同一个,所以不行

    后来看了下案例,不处理的话,就相当于把两个矩阵十字重叠起来,最后重叠的面积一定偏小,所以还是要处理

    还有就是精度问题??说是double存不下longlong的内容

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int maxn=1e5+7;
    typedef long long LL;
    struct Node{
        LL l,d;
        bool operator<(const Node &a){
            return d>a.d;
        }
    }node[maxn];
    int n;
    LL ans,l,d;
    int main(){
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%lld%lld",&l,&d);
            if(d<l)swap(d,l);
            node[i].d=d,node[i].l=l;
            ans=max(ans,l*d);
        }
        sort(node,node+n);
        LL maxl=node[0].l;
        for(int i=1;i<n;i++){
            ans=max(ans,node[i].d*min(maxl,node[i].l)*2);
            maxl=max(maxl,node[i].l);
        }
        if(ans%2)printf("%lld.5
    ",ans/2);
        else printf("%lld.0
    ",ans/2);
        return 0;
    }
    View Code

    CodeForces 1252K

    这道题很好想,题意是给出一串字符,和一堆操作

    1操作是反转l到r的字符,2是通过字符串得到f函数的值

    只要你按照题目说的做,很容易就能写出来,然后TLE

    然后我考虑把操作1写作前缀和,然后还是TLE

    问题只有可能出在f函数上了,但是。。想不出来


    要是思维灵敏一点,你就会发现A=A+B和B=A+B的操作可以分别用两种矩阵实现

    但是我们毕竟不能有几个‘A’就乘几个A=A+B的矩阵,毕竟字符串是有顺序的,‘AB’和‘BA’最后的结果是不一样的

    所以,画风一转,使用线段树来同时满足修改区间和得到有序的累积的区间值的操作

    具体一点,就是通过线段树就可以得到l到r的区间里所有两种矩阵的累乘

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    typedef long long LL;
    const int maxn=1e5+7;
    const LL mod=1e9+7;
    struct mat{
        LL m[3][3];
        mat(){
            memset(m,0,sizeof(m));
        }
    }A,B,st[maxn<<2][2];
    int lazy[maxn<<2],n,q,l,r,op;
    LL a,b;
    char s[maxn];
    mat operator*(mat a,mat b){
        mat ans;
        for(int i=1;i<=2;i++)
        for(int j=1;j<=2;j++)
        for(int k=1;k<=2;k++){
            ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod;
    //        ans.m[i][j]%=mod;
        }
        return ans;
    }
    void pushdown(int l,int r,int rt){
        if(lazy[rt]){
            swap(st[rt<<1][0],st[rt<<1][1]);
            swap(st[rt<<1|1][0],st[rt<<1|1][1]);
            lazy[rt<<1]^=1;
            lazy[rt<<1|1]^=1;
            lazy[rt]=0;
        }
    }
    void pushup(int rt){
        st[rt][0]=st[rt<<1][0]*st[rt<<1|1][0];
        st[rt][1]=st[rt<<1][1]*st[rt<<1|1][1];
    }
    void build(int l,int r,int rt){
        //lazy[rt]=0;
        if(l==r){
            if(s[l]=='A')
                st[rt][0]=A,st[rt][1]=B;
            else st[rt][0]=B,st[rt][1]=A;
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        pushup(rt);
    }
    void change(int l,int r,int rt,int ll,int rr){
        if(ll<=l&&r<=rr){
            swap(st[rt][0],st[rt][1]);
            lazy[rt]^=1;
            return;
        }
        int mid=(l+r)>>1;
        pushdown(l,r,rt);
        if(ll<=mid)change(l,mid,rt<<1,ll,rr);
        if(rr>mid)change(mid+1,r,rt<<1|1,ll,rr);
        pushup(rt);
    }
    mat query(int l,int r,int rt,int ll,int rr){
        if(ll<=l&&r<=rr)
            return st[rt][0];
        int mid=(l+r)>>1;
        mat ans;
        ans.m[1][1]=ans.m[2][2]=1;
        pushdown(l,r,rt);
        if(ll<=mid)
            ans=ans*query(l,mid,rt<<1,ll,rr);
        if(rr>mid)
            ans=ans*query(mid+1,r,rt<<1|1,ll,rr);
        return ans;
    }
    int main(){
        scanf("%d%d%s",&n,&q,s+1);
        A.m[1][1]=1,A.m[1][2]=0;
        A.m[2][1]=1,A.m[2][2]=1;
        B.m[1][1]=1,B.m[1][2]=1;
        B.m[2][1]=0,B.m[2][2]=1;
    //    scanf("%s",s+1);
        build(1,n,1);
    //    show(A*B);
        while(q--){
            scanf("%d%d%d",&op,&l,&r);
            if(op==1){
                change(1,n,1,l,r);
            }
            else {
                scanf("%lld%lld",&a,&b);
                mat kk=query(1,n,1,l,r);
                mat ans;
                ans.m[1][1]=a,ans.m[1][2]=b;
                ans=ans*kk;
                printf("%lld %lld
    ",ans.m[1][1],ans.m[1][2]);
            }
        }
        return 0;
    }
    View Code

    CodeForces 1220D

    做得多了我慢慢也开始明白了,当你一点思路都没有又是跟数有关的大多都是数论。

    这道题文字太长,一开始题意都没明白

    主要讲的就是在无穷的点集里,只要两点相差i值,B集合又有i值,就两点相连

    问你删除B集合哪些点后,连接的图是二分图


    这道题保证二分图就是要保证无奇环

    我们看一下两个互质的数,比如3,4

    从起点0开始,依次加3,变成0,3,6,9,12

    依次加4,变成0,4,8,12。在0-12的环里一共有7个数,是奇环

    上半圈有4条线,下半圈有3条,这是因为3,4互质,3要乘4变成12,4要乘3变成12

    所以第一个结论,若是互质的数,相加为奇数则有奇环

    若是奇偶互质,那必是奇环。所以可能的解只有是奇奇互质,比如3,5

    第二个结论,两个数同乘相同倍数,环线数不变

    比如3,5能组成8线环,12,20也能组成8线环,不同的是一条线增加的值变多了

    最后一个结论,所以我们把两个数除到互质,最后判断是不是两个奇数就行了

    但其实奇数除奇数还是奇数,所以看两个数除以相同倍数的2,最后看是不是两个奇数也行

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    const int maxn=2e5+7;
    typedef long long LL;
    LL n,a[maxn],cnt[maxn],num[maxn];
    int main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        for(int i=1;i<=n;i++){
            LL t=a[i];
            while(t%2==0){
                cnt[i]++;
                t/=2;
            }
            num[cnt[i]]++;
        }
        int ans=0,maxcnt;
        for(int i=0;i<63;i++)
        if(num[i]>ans){
            ans=num[i];
            maxcnt=i;
        }
        printf("%lld
    ",n-ans);
        for(int i=1;i<=n;i++)
            if(cnt[i]!=maxcnt)printf("%lld ",a[i]);
        cout<<endl;
        return 0;
    }
    View Code

    CodeForces 1225D

    转载

  • 相关阅读:
    Android SDK Android NDK 官方下载地址
    编码转换工具 源码
    st_mode的剖析
    关于 python 字符编码的一些认识
    MFC中的argc和argv参数
    VC实现文件拖拽获取文件名
    CString 转 int
    《C语言程序设计实践教程》实验题源程序
    C语言 文件操作 结构体与文件 fgetc fputc fread fwrite
    C++语言 创建状态栏
  • 原文地址:https://www.cnblogs.com/helman/p/11873453.html
Copyright © 2020-2023  润新知