zoukankan      html  css  js  c++  java
  • 2019.9.29 csp-s模拟测试55 反思总结

    不咕咕咕是一种美德【大雾】

    头一次体会到爆肝写题解???

    这次考试我们没赶上,是后来掐着时间每个人自己考的。我最后的分数能拿到152…熟悉的一题AC两题爆炸。

    强烈吐槽出题人起名走心

    T1联:

    发现每一次加入一个区间的操作,只有区间的l或者r+1有可能成为答案。那么考虑能不能用这两个点代表一整个区间,维护全局最靠左的0在什么地方。

    把每个操作的l和r+1都存下来,离散化,建一棵线段树。每一次区间操作都针对线段树上的a[l]-a[r+1]-1这部分(a[x]为x离散化以后的排序,即线段树里的位置)。

    维护线段树的区间和,每一次查询就是寻找最左边的区间和不等于区间长度的地方。对于反转操作,打一个可下传的标记,让现有区间和变成区间长度减去原区间和。

    要注意一开始答案为1,保存下所有l和r+1还要额外加一个1。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int m,cnt=1;
    long long b[300010];
    struct node{
        int opt;
        long long l,r;
    }a[300010];
    struct tree{
        int l,r,sum,tag1,tag2;
    }t[1000010];
    void build(int p,int l,int r){
        t[p].l=l,t[p].r=r;
        t[p].tag1=-1;
        if(l==r)return;
        int mid=(l+r)/2;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
    }
    void pushdown(int p){
        if(t[p].tag1!=-1){
            t[p*2].tag1=t[p].tag1;
            t[p*2].sum=(t[p*2].r-t[p*2].l+1)*t[p].tag1;
            t[p*2].tag2=0;
            t[p*2+1].tag1=t[p].tag1;
            t[p*2+1].sum=(t[p*2+1].r-t[p*2+1].l+1)*t[p].tag1;
            t[p*2+1].tag2=0;
            t[p].tag1=-1;
        }
        if(t[p].tag2){
            t[p*2].tag2^=1;
            t[p*2].sum=t[p*2].r-t[p*2].l+1-t[p*2].sum;
            t[p*2+1].tag2^=1;
            t[p*2+1].sum=t[p*2+1].r-t[p*2+1].l+1-t[p*2+1].sum;
            t[p].tag2=0;
        }
    }
    void change(int p,int l,int r,int val){
        if(l<=t[p].l&&t[p].r<=r){
            t[p].sum=(t[p].r-t[p].l+1)*val;
            t[p].tag1=val;
            t[p].tag2=0;
            return;
        }
        pushdown(p);
        int mid=(t[p].l+t[p].r)/2;
        if(l<=mid)change(p*2,l,r,val);
        if(r>mid)change(p*2+1,l,r,val);
        t[p].sum=t[p*2].sum+t[p*2+1].sum;
    }
    void change1(int p,int l,int r){
        if(l<=t[p].l&&t[p].r<=r){
            t[p].tag2^=1;
            t[p].sum=t[p].r-t[p].l+1-t[p].sum;
            return;
        }
        pushdown(p);
        int mid=(t[p].l+t[p].r)/2;
        if(l<=mid)change1(p*2,l,r);
        if(r>mid)change1(p*2+1,l,r);
        t[p].sum=t[p*2].sum+t[p*2+1].sum;
    }
    long long query(int p){
        if(t[p].l==t[p].r){
            return b[t[p].l];
        }
        pushdown(p);
        int mid=(t[p].l+t[p].r)/2;
        long long val;
        if(t[p*2].sum!=t[p*2].r-t[p*2].l+1)val=query(p*2);
        else if(t[p*2+1].sum!=t[p*2+1].r-t[p*2+1].l+1)val=query(p*2+1);
        t[p].sum=t[p*2].sum+t[p*2+1].sum;
        return val;
    }
    int main()
    {
        scanf("%d",&m);
        b[1]=1;
        for(int i=1;i<=m;i++){
            scanf("%d%lld%lld",&a[i].opt,&a[i].l,&a[i].r);
            a[i].r++;
            b[++cnt]=a[i].l;
            b[++cnt]=a[i].r;
        }
        sort(b+1,b+cnt+1);
        int n=unique(b+1,b+cnt+1)-b-1;
        build(1,1,n);
        for(int i=1;i<=m;i++){
            a[i].l=lower_bound(b+1,b+n+1,a[i].l)-b;
            a[i].r=lower_bound(b+1,b+n+1,a[i].r)-b;
            if(a[i].opt==1)change(1,a[i].l,a[i].r-1,1);
            else if(a[i].opt==2)change(1,a[i].l,a[i].r-1,0);
            else change1(1,a[i].l,a[i].r-1);
            printf("%lld
    ",query(1));
        }
        return 0;
    }
    View Code

    T2赛:

    考试的时候错误贪心骗到了40分…

    正解是枚举满足两个人都喜欢的物品的个数x,然后对于每个人再补上k-x个他喜欢的物品,不足m个就再从剩下的物品里补全。用线段树维护剩下的物品以支持查询前多少个最小值的和。

    注意边界问题。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    using namespace std;
    int n,m,k,A,B,cnt,k1,k2;
    int v[200010],v1[200010],va[200010],vb[200010],val[200010];
    int a[200010],b[200010],ab[200010],d[200010];
    long long sum,ans=1e18,suma[200010],sumb[200010],sumab;
    struct node{
        int l,r,cnt;
        long long sum;
    }t[1000010];
    void build(int p,int l,int r){
        t[p].l=l,t[p].r=r;
        if(l==r)return;
        int mid=(l+r)/2;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
    }
    void change(int p,int l,int r,int y){
        if(l<=t[p].l&&t[p].r<=r){
            t[p].cnt+=y;
            t[p].sum+=y*(val[l]);
            return;
        }
        int mid=(t[p].l+t[p].r)/2;
        if(l<=mid)change(p*2,l,r,y);
        if(r>mid)change(p*2+1,l,r,y);
        t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
        t[p].sum=t[p*2].sum+t[p*2+1].sum;
    }
    long long query(int p,int y){
        if(t[p].l==t[p].r){
            return t[p].sum/t[p].cnt*y;
        }
    //    int mid=(t[p].l+t[p].r)/2;
        if(t[p*2].cnt>y)return query(p*2,y);
        else if(t[p*2].cnt==y)return t[p*2].sum;
        else{
            long long z=0;
            z+=t[p*2].sum;
            return z+query(p*2+1,y-t[p*2].cnt);
        }
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++){
            scanf("%d",&v[i]);
            val[++cnt]=v[i];
        }
        sort(val+1,val+cnt+1);
        int num=unique(val+1,val+cnt+1)-val-1;
        scanf("%d",&A);
        for(int i=1,x;i<=A;i++){
            scanf("%d",&x);
            va[x]=1;
        }
        scanf("%d",&B);
        for(int i=1,x;i<=B;i++){
            scanf("%d",&x);
            vb[x]=1;
        }
        if(A<k||B<k||k>m||n<m){
            printf("-1");
            return 0;
        }
        for(int i=1;i<=n;i++){
            v1[i]=lower_bound(val+1,val+num+1,v[i])-val;
            if(va[i]&&vb[i]){
                ab[++ab[0]]=v1[i];
            }
            else if(va[i]){
                a[++a[0]]=v1[i];
            }
            else if(vb[i]){
                b[++b[0]]=v1[i];
            }
            else d[++d[0]]=v1[i];
        }
        if(k*2-ab[0]>m){
            printf("-1");
            return 0;
        }
        build(1,1,num);
        sort(ab+1,ab+ab[0]+1);
        sort(a+1,a+a[0]+1);
        sort(b+1,b+b[0]+1);
        sort(d+1,d+d[0]+1);
        for(int i=1;i<=a[0];i++){
            suma[i]=suma[i-1]+val[a[i]];
            if(i>k)change(1,a[i],a[i],1);
        }
        for(int i=1;i<=b[0];i++){
            sumb[i]=sumb[i-1]+val[b[i]];
            if(i>k)change(1,b[i],b[i],1);
        }
    //    for(int i=1;i<=ab[0];i++){
    //        change(1,ab[i],ab[i],1);
    //    }
        for(int i=1;i<=d[0];i++){
            change(1,d[i],d[i],1);
        }
        for(int i=0;i<=min(ab[0],k);i++){
            if(2*k-m>i||k-i>a[0]||k-i>b[0]){
                if(i>0){
    //                change(1,ab[i],ab[i],-1);
                    sumab+=val[ab[i]];
                    if(a[k-i+1])change(1,a[k-i+1],a[k-i+1],1);
                    if(b[k-i+1])change(1,b[k-i+1],b[k-i+1],1);
                }
            }
            else if(i==0){
                sum=suma[k]+sumb[k];
                sum+=query(1,m-2*k);
                ans=min(sum,ans);
            }
            else{
    //            change(1,ab[i],ab[i],-1);
                sumab+=val[ab[i]];
                if(a[k-i+1])change(1,a[k-i+1],a[k-i+1],1);
                if(b[k-i+1])change(1,b[k-i+1],b[k-i+1],1);
                sum=sumab+suma[k-i]+sumb[k-i];
                sum+=query(1,m-i-2*(k-i));
                ans=min(ans,sum);
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    T3题:

    对于每个苹果,考虑它如果要存活下来需要满足哪些条件,求出它如果存活需要ban掉的苹果的集合。最后统计答案,枚举两个苹果,如果它们都有机会存活且存活需要ban掉的集合没有交集,那么答案++。

    于是一开始假设一个苹果最后活着,从后往前逆推出需要满足的集合。逆推的意义为,若要让现有集合存活到这个操作之后,需要让前面的集合满足什么样子。

    如果一个操作的两个苹果都属于现有集合,那么作为目标存活到最后的苹果显然必死。如果一个操作中有一个苹果属于现有集合,那么为了让这个苹果存活下来以满足后面的操作,另一个苹果肯定属于这个操作之前的集合。

    考试的时候想到了集合也想到了逆推,愣是没搞出来怎么写,大约是含义还有不明确的地方。

    #include<iostream>
    #include<cstdio>
    #include<bitset>
    #include<cstring>
    using namespace std;
    int n,m,vis[410],flag,ans,x[50010],y[50010],num;
    bitset<410>a[410];
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x[i],&y[i]);
        }
        for(int i=1;i<=n;i++){
            a[i][i]=1;
            for(int j=m;j>=1;j--){
                int u=x[j],v=y[j];
                if(a[i][u]&&a[i][v]){
                    vis[i]=1;
                    break;
                }
                else if(a[i][u]||a[i][v]){
                    a[i][u]=a[i][v]=1;
                }
            }
        }    
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(!vis[i]&&!vis[j]&&(a[i]&a[j]).count()==0)ans++;
            }
        }
        printf("%d",ans);
        return 0;
    }
    View Code

    几次考试暴露出来的问题越来越多,有点应付不过来。

    趁着国庆集训赶紧追进度…不然怕是真的要在一个多月以后彻底结束了。

  • 相关阅读:
    PHP使用数据库永久连接方式操作MySQL的是与非
    php生成xml文件
    Ruby学习之类
    新增题目功能模块总结
    Ruby学习之类2
    smarty section循环成两列的问题
    jQuery validate插件初探
    Zend Framework学习之Zend_Config
    Zend Framework学习之Zend_Loader动态加载文件和类
    JS 删除字符串最后一个字符的方法
  • 原文地址:https://www.cnblogs.com/chloris/p/11614792.html
Copyright © 2011-2022 走看看