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\n",ans); return 0; }
#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\n",ans); return 0; }
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\n",ans); return 0; }
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\n",pos); for(int i=1;i<aslen;i++)printf("%d ",as[i]); printf("%d\n",as[aslen]); return 0; } } printf("-1\n"); return 0; }
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("\n"); for(int i=2;i<=len;i+=2)printf("%d %d %d\n",a[i].x,a[i].y,a[i].c);*/ int ans=0; while(bt_h()) { ans+=findflow(st,(1<<30)); /*printf("\n"); for(int i=2;i<=len;i+=2)printf("%d %d %d\n",a[i].x,a[i].y,a[i].c); printf("%d\n",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; }
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\n",ans); } return 0; }
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\n"); for(int i=1;i<n;i++)printf("1 "); printf("1\n"); 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\n"); for(int i=1;i<n;i++)printf("%d ",as[i]); printf("%d\n",as[n]); } return 0; }