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\n",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\n",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

 

 

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

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

 

posted @ 2019-10-01 09:09  Chloris_Black  阅读(206)  评论(0编辑  收藏  举报