zoukankan      html  css  js  c++  java
  • codeforce1070 2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred) 题解

     

    秉承ACM团队合作的思想,这篇blog只有部分题解,剩余的请前往星感大神Star_Feel的blog食用(表示男神汉克斯更懒不屑于写我们分别代写了下...)

    C. Cloud Computing 

    扫描线搞一搞区间(主席树也OK啊,只是空间玄学,主席树理论空间nlogn实际上开小那么10倍8倍没什么锅啊zzzz),对于权值建立权值线段树,然后记录每个权值出现的次数以及区间权值和,然后在线段树上二分求答案即。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=1000010,M=10000010;
    struct node {
        int lc,rc;ll c; ll sum;
    } t[M]; int rt[N],cnt;
    void add(int &u,int l,int r,int c,int p,int pp) {
        if(u==0) u=++cnt; t[u].c+=c; t[u].sum+=ll(c)*ll(pp);
        if(l==r) return ; int mid=(l+r)/2;
        if(p<=mid) add(t[u].lc,l,mid,c,p,pp);
        else add(t[u].rc,mid+1,r,c,p,pp);
    }
    void Merge(int &u1,int u2) {
        if(u1==0) { u1=u2; return ; } if(u2==0) return ;
        t[u1].c+=t[u2].c; t[u1].sum+=t[u2].sum;
        Merge(t[u1].lc,t[u2].lc); Merge(t[u1].rc,t[u2].rc);
    }
    int n,K,ys[200010];
    ll find(int u,int l,int r,int k) {
        if(t[u].c<=k) return t[u].sum;
        if(l==r) return ll(k)*ll(ys[l]);
        int mid=(l+r)/2;
        if(t[t[u].lc].c>=k) return find(t[u].lc,l,mid,k);
        else return t[t[u].lc].sum+find(t[u].rc,mid+1,r,k-t[t[u].lc].c);
    }
    int L[200010],R[200010],C[200010],P[200010],s[200010];
    struct ls {
        int x,id;
    } A[200010];
    bool cmp(ls n1,ls n2) { return n1.x<n2.x; }
    int main() {
        scanf("%d%d",&n,&K);
        int m; scanf("%d",&m);
        cnt=0; memset(rt,0,sizeof(rt));
        int t=0;
        for(int i=1;i<=m;i++) {
            int l,r,c,p; scanf("%d%d%d%d",&L[i],&R[i],&C[i],&P[i]);
            A[++t]=(ls){P[i],i};
        }
        sort(A+1,A+1+t,cmp); int tot=0; 
        for(int i=1;i<=t;i++) {
            if(A[i].x!=A[i-1].x) tot++;
            s[A[i].id]=tot; ys[tot]=A[i].x;
        }
        for(int i=1;i<=m;i++) {
            add(rt[L[i]],1,tot,C[i],s[i],P[i]);
            add(rt[R[i]+1],1,tot,-C[i],s[i],P[i]);
        }
        for(int i=2;i<=n;i++) Merge(rt[i],rt[i-1]);
        ll ans=0;
        for(int i=1;i<=n;i++)  ans+=find(rt[i],1,tot,K);
        printf("%lld
    ",ans);
        return 0;
    }
    C. Cloud Computing(主席树by_hanks_o)
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    LL read()
    {
        LL x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x;
    }
    struct node
    {
        int l,r,lc,rc;LL c,s;
    }tr[2100000];int trlen;
    void bt(int l,int r)
    {
        int now=++trlen;
        tr[now].l=l;tr[now].r=r;
        tr[now].lc=tr[now].rc=-1;
        tr[now].c=0;tr[now].s=0;
        if(l<r)
        {
            int mid=(l+r)/2;
            tr[now].lc=trlen+1;bt(l,mid);
            tr[now].rc=trlen+1;bt(mid+1,r);
        }
    }
    void change(int now,LL p,LL c)
    {
        if(tr[now].l==tr[now].r)
        {
            tr[now].c+=c;
            tr[now].s=(LL)tr[now].l*tr[now].c;
            return ;
        }
        
        int mid=(tr[now].l+tr[now].r)/2;
        int lc=tr[now].lc,rc=tr[now].rc;
        
        if(p<=mid)change(lc,p,c);
        else       change(rc,p,c);
        
        tr[now].c=tr[lc].c+tr[rc].c;
        tr[now].s=tr[lc].s+tr[rc].s;
    }
    LL findans(int now,LL k)
    {
        if(tr[now].c<=k)return tr[now].s;
        if(tr[now].l==tr[now].r)return k*tr[now].l;
        
        int mid=(tr[now].l+tr[now].r)/2;
        int lc=tr[now].lc,rc=tr[now].rc;
        
        if(tr[lc].c>=k)return findans(lc,k);
        else return tr[lc].s+findans(rc,k-tr[lc].c);
    }
    
    struct line
    {
        int id;LL c,p;
        line(){}
        line(int ID,LL C,LL P){id=ID;c=C;p=P;}
    }li[410000];int len;
    bool cmp(line l1,line l2){return l1.id<l2.id;}
    int main()
    {
        int n,k,m,l,r;LL c,p;
        n=read(),k=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            l=read(),r=read(),c=read(),p=read();
            li[++len]=line(l,c,p);
            li[++len]=line(r+1,-c,p);
        }
        
        LL ans=0;int tp=1;
        sort(li+1,li+len+1,cmp);
        trlen=0;bt(1,1000000);
        for(int i=1;i<=n;i++)
        {
            while(tp<=len&&li[tp].id==i)change(1,li[tp].p,li[tp].c),tp++;
            ans+=findans(1,k);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    C. Cloud Computing(扫描线)

    D. Garbage Disposal

    强行贪心,>K的直接就扔了,然后剩下的留到后面扔(直接并到后一天的垃圾里面),但是要判断一下今天剩下的还有没有包含前一天的,有的话只能加一个袋子

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=200010;
    int a[N];
    typedef long long ll;
    int main() {
        int n,k; scanf("%d%d",&n,&k);
        ll ans=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int last=0;
        for(int i=1;i<n;i++) { 
            ans+=a[i]/k; 
            int now=a[i]%k;
            if(a[i]-last>=now) { last=now; a[i+1]+=last; }
            else { last=0; ans++; }
        }
        ans+=a[n]/k; if(a[n]%k!=0) ans++; printf("%lld
    ",ans);
        return 0;
    }
    D. Garbage Disposal(by_hanks_o)

    G. Monsters and Potions

    这就是一个O(n^4)的硬模拟,枚举集合点,不断重复枚举每个英雄,O(n)判断是否可达以及修改点权

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    int p[110],h[110],dd[110],d[110];bool v[110];
    int aslen,as[110];
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)scanf("%d%d",&p[i],&h[i]);
        for(int i=1;i<=n;i++)scanf("%d",&dd[i]);
        
        for(int pos=1;pos<=n;pos++)
        {
            bool bk=true;aslen=0;
            memset(v,false,sizeof(v));
            memcpy(d,dd,sizeof(d));
            while(bk==true)
            {
                bk=false;
                for(int i=1;i<=m;i++)
                {
                    if(v[i]==true)continue;
                    if(p[i]>=pos)
                    {
                        bool flag=true;
                        int k=h[i];
                        for(int j=p[i];j>=pos;j--)
                        {
                            k+=d[j];
                            if(k<0){flag=false;break;}
                        }
                        if(flag==true)
                        {
                            as[++aslen]=i;v[i]=true;bk=true;
                            for(int j=pos;j<=p[i];j++)d[j]=0;
                        }
                    }
                    else
                    {
                        bool flag=true;
                        int k=h[i];
                        for(int j=p[i];j<=pos;j++)
                        {
                            k+=d[j];
                            if(k<0){flag=false;break;}
                        }
                        if(flag==true)
                        {
                            as[++aslen]=i;v[i]=true;bk=true;
                            for(int j=p[i];j<=pos;j++)d[j]=0;
                        }
                    }
                }
            }
            if(aslen==m)
            {
                printf("%d
    ",pos);
                for(int i=1;i<aslen;i++)printf("%d ",as[i]);
                printf("%d
    ",as[aslen]);
                return 0;
            }
        }
        printf("-1
    ");
        return 0;
    }
    G. Monsters and Potions

    I. Privatization of Roads in Berland

    这题搞了我一天。。%得头皮发麻(主要是没有官方题解啊qwq而且眼瞎没看到discuss有人发了)

    首先,两条相同公司的边肯定是放在一起的,这样才能尽可能的让一个点周围的公司较少

    (此处作出贡献对的是这个点只被1个公司的边连向的个数)

    这样的话,对于每一条边,会对1个点作出贡献。

    考虑对于du[i]<=k的点,它最多接受du[i]的贡献

    对于k<du[i]<=2*k的点,我们最多让用k条边每个公司都出现一次,然后多出来的du[i]-k条边和前面的边匹配,那么单独的边的条数就是2*k-du[i]

    du[i]>2*k必然无解

    网络流一手,st连向边流量为1,边连向它连接的两点流量为1,表示它只能对其中一个点作出贡献,另一个点一定可以找到一条和它同个公司的边。点流向ed流量为最多接受的贡献,通过残余网络,需要判断一下每条边是否都用上了

    对于输出方案,通过残余网络我们可以知道每条边选了哪个点,考虑到对于这个点我这条边上的公司是单独的,那另一边就是成对的,就让这条边到另一边去匹配

    通过discuss我又了解到一种做法,对于k<du[i]<=2*k的点,我们必须找到至少(k-du[i])*2条边两两属于同一个公司,也就是相当于多重匹配问题?

    同样是网络流,st连向边流量为1,边连向它连接的两点流量为1,表示它也是只能用于其中一个点的两两配对,点流向ed流量为(k-du[i])*2

    这样的话,你还需要判断是否满流

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void write(int d)
    {
        if(d<0){putchar('-');d=-d;}
        if(d>=10)write(d/10);
        putchar(d%10+'0');
    }
    
    int n,m,K;
    struct node
    {
        int x,y,c,next;
    }a[41000];int len,last[1500];
    void ins(int x,int y,int c)
    {
        len++;
        a[len].x=x;a[len].y=y;a[len].c=c;
        a[len].next=last[x];last[x]=len;
        
        len++;
        a[len].x=y;a[len].y=x;a[len].c=0;
        a[len].next=last[y];last[y]=len;
    }
    int st,ed,h[1500],list[1500];
    bool bt_h()
    {
        int head=1,tail=2;list[1]=st;
        memset(h,0,sizeof(h));h[st]=1;
        while(head!=tail)
        {
            int x=list[head];
            for(int k=last[x];k;k=a[k].next)
            {
                int y=a[k].y;
                if(a[k].c>0&&h[y]==0)
                {
                    h[y]=h[x]+1;
                    list[tail++]=y;
                }
            }
            head++;
        }
        if(h[ed]==0)return false;
        return true;
    }
    int findflow(int x,int f)
    {
        if(x==ed)return f;
        int s=0;
        for(int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if(a[k].c>0&&h[y]==h[x]+1&&s<f)
            {
                int t=findflow(y,min(a[k].c,f-s));
                s+=t;a[k].c-=t;a[k^1].c+=t;
            }
        }
        if(s==0)h[x]=0;
        return s;
    }
    
    //-------------------------------------------dicnic------------------------------------
    
    int u[1500],v[1500],du[1500];
    bool check()
    {
        int c=0;
        for(int i=1,k=3;i<=m;i++,k+=2)c+=a[k].c;
        return c==m;
    }
    vector<int>vec[1500];
    int as[1500];
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        int T;
        T=read();
        while(T--)
        {
            n=read(),m=read(),K=read();
            len=1;memset(last,0,sizeof(last));
            st=n+m+1,ed=n+m+2;
            memset(du,0,sizeof(du));
            for(int i=1;i<=m;i++)
            {
                u[i]=read(),v[i]=read();
                ins(st,i+n,1);
                du[u[i]]++,du[v[i]]++;
            }
            for(int i=1;i<=m;i++)
                ins(i+n,u[i],1),ins(i+n,v[i],1);
            bool flag=false;
            for(int i=1;i<=n;i++)
            {
                if(du[i]<=K)ins(i,ed,du[i]);
                else if(du[i]<=2*K)ins(i,ed,2*K-du[i]);
                else 
                {
                    for(int i=1;i<m;i++)putchar('0'),putchar(' ');
                    putchar('0'),puts("");
                    flag=true;break;
                }
            }
            if(flag==true)continue;
            
            /*printf("
    ");
            for(int i=2;i<=len;i+=2)printf("%d %d %d
    ",a[i].x,a[i].y,a[i].c);*/
            int ans=0;
            while(bt_h())
            {
                ans+=findflow(st,(1<<30));
                /*printf("
    ");
                for(int i=2;i<=len;i+=2)printf("%d %d %d
    ",a[i].x,a[i].y,a[i].c);
                printf("%d
    ",ans);*/
            }
            if(!check())
            {
                for(int i=1;i<m;i++)putchar('0'),putchar(' ');
                putchar('0'),puts("");
                continue;
            }
            
            for(int i=1;i<=n;i++)vec[i].clear();
            for(int i=1,k=1+2*m+2;i<=m;i++,k+=4)
            {
                if(a[k].c==0)vec[u[i]].push_back(i);
                else         vec[v[i]].push_back(i);
            }
            int cnt=0;
            for(int i=1;i<=n;i++)
                for(int j=0;j<vec[i].size();j++)
                {
                    if(j%2==0)cnt++;
                    as[vec[i][j]]=cnt;
                }
            for(int i=1;i<m;i++)write(as[i]),putchar(' ');
            write(as[m]);puts("");
        }
        return 0;
    }
    I. Privatization of Roads in Berland

    J. Streets and Avenues in Berhattan

    考虑一个性质,对于最后不方便度的贡献只会来自于1种字母,否则我们可以通过两个不同字母的行列交换满足这一条件

    枚举这一个字母,因为其他的字母都可以全部放满了,对行进行背包,f[k]表示行有k个已经被命名是否可行

    假如k可行,剩下的行就有n-k个,因为我们是i这个字母不放,剩下都可以放列,所以剩余列数就是m-(K-c[i]-k)

    求剩余行和列乘积的最小值即可

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    char ss[210000];
    int c[30];
    bool f[31000];
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int n,m,K;
            scanf("%d%d%d",&n,&m,&K);
            scanf("%s",ss+1);
            memset(c,0,sizeof(c));
            for(int i=1;i<=K;i++)c[ss[i]-'A'+1]++;
            
            int ans=(1<<30);
            for(int i=1;i<=26;i++)
            {
                memset(f,false,sizeof(f));f[0]=true;
                for(int j=1;j<=26;j++)
                {
                    if(i==j||c[j]==0)continue;
                    for(int k=n;k>=c[j];k--)f[k]|=f[k-c[j]];
                }
                
                for(int k=0;k<=n;k++)
                    if(f[k]==true)
                    {
                        int h=max(0,n-k),l=max(0,m-(K-c[i]-k));
                        if(h+l<=c[i])ans=min(ans,h*l);
                    }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    J. Streets and Avenues in Berhattan

    L. Odd Federalization

    一道挺神的题,假如是考场做就只能暴力猜结论了??

    结论是一个图拆分成r个诱导子图并且满足每个点的度数都为偶数,r<=2

    r=1明显直接做

    r=2对于每个点都有0或1两种取值,若一个点度数为偶数,那么要满足条件的话所有它连向的点的权值异或和应该等于0

    若为奇数,则所有它连向的点的权值异或和再异或当前点的点权应该等于1

    高斯消元解异或方程可踩此题

    那么为什么呢,反证它一手

    考虑对于高斯消元无解的情况,是出现0=1

    右边是1证明产生选出来的这些柿子的点,有奇数个度数为奇数的点

    左边是0证明所有参与了运算的点的系数都是偶数,那么也包括上面那奇数个度数为奇数的点,而剩下的点的度数都应该为偶数

    用这个算出来,这些点组成的图的度数就是奇数了(偶*偶+奇*奇=奇数),然而对于无向图度数一定是偶数QWQ

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<bitset>
    using namespace std;
    
    int n,m,du[2100];
    bool check()
    {
        for(int i=1;i<=n;i++)
            if(du[i]%2==1)return false;
            
        printf("1
    ");
        for(int i=1;i<n;i++)printf("1 ");
        printf("1
    ");
        return true;
    }
    bitset<2100>s[2100];
    void gauss()
    {
        int x=1;
        for(int j=1;j<=n;j++)
        {
            for(int i=x;i<=n;i++)
                if(s[i][j]==1){swap(s[i],s[x]);break;}
            if(s[x][j]==0)continue;
                
            for(int i=1;i<=n;i++)
            {
                if(i==x)continue;
                if(s[i][j]==1)s[i]^=s[x];
            }
            x++;
        }
    }
    int as[2100];
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int x,y;
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)s[i].reset();
            memset(du,0,sizeof(du));
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&x,&y);
                du[x]++,du[y]++;
                s[x][y]=1,s[y][x]=1;
            }
            if(check())continue;
            
            for(int i=1;i<=n;i++)
                if(du[i]%2==1)s[i][i]=1,s[i][n+1]=1;
            gauss();
            x=1;
            for(int j=1;j<=n;j++)
            {
                if(s[x][j]==0){as[j]=1;continue;}
                as[j]=s[x][n+1]+1;x++;
            }
            
            printf("2
    ");
            for(int i=1;i<n;i++)printf("%d ",as[i]);
            printf("%d
    ",as[n]);
        }
        return 0;
    }
    L. Odd Federalization
  • 相关阅读:
    对于近期学习上的复习与整理
    ACM的奇计淫巧_输入挂
    hdu2602 DP (01背包)
    hdu 1723 DP/递推
    hdu1428 记忆化搜索(BFS预处理最短路径和+DP+DFS)
    hdu1355
    hdu1331&&hdu1579记忆化搜索(DP+DFS)
    hdu1257 dp(最长上升子序列)
    hdu1208 dp
    hdu 1203 dp(关于概率的```背包?)
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/9834706.html
Copyright © 2011-2022 走看看