2019.9.29 csp-s模拟测试55 反思总结

Posted chloris

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

 

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

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

 

以上是关于2019.9.29 csp-s模拟测试55 反思总结的主要内容,如果未能解决你的问题,请参考以下文章

2019.10.26 csp-s模拟测试88 反思总结

[考试反思]1006csp-s模拟测试61:休止

2019.9.20 csp-s模拟测试48 反思总结

[考试反思]1110csp-s模拟测试108:消遣

[CSP-S模拟测试48]反思+题解

[考试反思]1002csp-s模拟测试57:平庸