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\n",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\n");
            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\n");
        }
        else printf("NO\n");
    }
    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\n",ans/2);
    else printf("%lld.0\n",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\n",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",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

转载

posted @ 2019-11-16 20:36  helman78  阅读(171)  评论(0编辑  收藏  举报