usaco 2017 US Open
Posted FallDream
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了usaco 2017 US Open相关的知识,希望对你有一定的参考价值。
A:Modern Art
给定一个n*n的网格,有n*n个颜色,每种颜色按一定顺序覆盖了一个矩形。给定末状态,求有几种颜色可能是第一个填上去的。n<=1000
题解:二维查分+前缀和起来,然后就可以快速求得每个点被覆盖了多少次啦。复杂度n^2
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } int n,ans=0; int x1[1005],x2[1005],y1[1005],y2[1005]; int f[1005][1005],s[1005][1005]; bool b[1005]; int main() { freopen("art.in","r",stdin);freopen("art.out","w",stdout); n=read(); for(int i=1;i<=n*n;i++)x2[i]=y2[i]=0,x1[i]=y1[i]=1005; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int x=read();s[i][j]=x;if(!x)continue; if(!b[x])ans++,b[x]=1;x1[x]=min(x1[x],i);x2[x]=max(x2[x],i); y1[x]=min(y1[x],j);y2[x]=max(y2[x],j); } if(ans==1)return 0*printf("%d",n*n-1);ans=n*n; for(int i=1;i<=n*n;i++)if(b[i]) { f[x1[i]][y1[i]]++;f[++x2[i]][y1[i]]--; f[x1[i]][++y2[i]]--;f[x2[i]][y2[i]]++; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)f[i][j]+=f[i-1][j]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)f[i][j]+=f[i][j-1]; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(s[i][j]&&b[s[i][j]]) { int x=s[i][j]; if(f[i][j]>1) b[x]=0,ans--; } cout<<ans; return 0; }
B.Switch Grass
给定一个n个点m条边的图,每个点有一个颜色,边有边权。q个操作,每次修改一个点的颜色,询问连接的两个点颜色不同的权值最小的边的权值。n,m,q<=200000
先提供一种玄学做法,最坏n^2,但是usaco数据弱,也A了。
我们先把边按照权值排序,然后用fail[i]表示前i条边都不合法的情况下,下一条颜色可能不同的边是哪条。这个可以通过并查集处理出来。然后记录每个点最先出现的边的编号。每次修改,看一下修改的点的编号是否比答案小,如果小,那条边一定成为答案。如果不小的话,看一下是否把现在的答案这条边改成了相同的颜色,如果相同就沿着fail指针找到第一个不同的点,复杂度最坏n^2,但是由于数据弱,并且很多操作是O(1)的,所以卡过了,最久的点只跑了1074ms。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define MAXN 200000 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } int n,m,k,q,ans; int fail[MAXN+5]; int s[MAXN+5],col[MAXN+5],first[MAXN+5]; struct edge{ int to,from,w,num; }e[MAXN+5]; bool cmp(edge x,edge y){return x.w<y.w;} int getfa(int x){return s[x]==x?x:s[x]=getfa(s[x]);} bool combine(int x,int y){x=getfa(x);y=getfa(y);if(x!=y)s[x]=y;} bool check(int x,int y){return getfa(x)==getfa(y);} int main() { freopen("grass.in","r",stdin);freopen("grass.out","w",stdout); n=read();m=read();k=read();q=read(); for(register int i=1;i<=m;i++) {e[i].from=read();e[i].to=read();e[i].w=read();e[i].num=i;} sort(e+1,e+m+1,cmp); for(int i=1;i<=n;i++)s[i]=i; for(register int i=1,j=2;i<=m;i++) { combine(e[i].from,e[i].to); for(;j<=m&&check(e[j].from,e[j].to);)++j; fail[i]=j; }memset(first,127,sizeof(first)); for(register int i=1;i<=n;i++)col[i]=read(); for(register int i=1;i<=m;i++){first[e[i].to]=min(first[e[i].to],i);first[e[i].from]=min(first[e[i].from],i);} for(register int i=1;i<=m;i++) if(col[e[i].from]!=col[e[i].to]){ans=i;break;} for(register int i=1;i<=q;i++) { int x=read(),y=read();col[x]=y; if(first[x]<ans)ans=first[x]; else while(col[e[ans].from]==col[e[ans].to])ans=fail[ans]; printf("%d\n",e[ans].w); } return 0; }
官方题解:我们可以发现对答案有贡献的边只会在最小生成树上,所以先跑出mst然后给他定一个根。然后我们对每一个点,对它儿子所有可能的颜色都建一个堆。
这样修改一个点只要从它父亲那里删掉一个点然后重新扔一个进去就可以了。这样每个点的答案就是和它颜色不同的堆的最小值,之后我们再对整体建一个堆,把每个
堆的答案扔进去,就可以啦。
我觉得堆有点难受,所以对每个节点建了个平衡树,还作死写了splay,之后再用线段树搞起来,差点T了.....复杂度nlogn 跑得比暴力还慢...
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define MAXN 1500000 #define MAXL 200000 #define N 262144 #define INF 2000000000 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘) f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘; ch=getchar();} return x*f; } int n,m,qq,cnt=0; int fa[MAXN+5],fat[MAXL+5],c[MAXN+5][2],s[MAXN+5],head[MAXL+5],ans[MAXN+5]; int rt[MAXL+5],col[MAXN+5],num[MAXN+5],mx[MAXN+5],size[MAXN+5],id[MAXN+5],upx[MAXN+5]; int T[N*2+5]; struct edge{ int from,to,w; }e[200005]; struct edge_type_2{ int next,to,w; }e2[400005]; bool cmp(edge x,edge y){return x.w<y.w;} inline int getfa(int x){return !s[x]?x:s[x]=getfa(s[x]);} void add(edge x) { int f=x.from,t=x.to,w=x.w; e2[++cnt].next=head[f];e2[cnt].to=t; head[f]=cnt;e2[cnt].w=w; e2[++cnt].next=head[t];e2[cnt].to=f; head[t]=cnt;e2[cnt].w=w; } inline void mst() { int x,y; for(register int i=1;i<=m;i++) { x=getfa(e[i].from);y=getfa(e[i].to); if(x!=y)s[x]=y,add(e[i]); } } void renew(int x,int ad) { // cout<<"renew"<<x<<" "<<ad<<endl; T[x+=N]=ad; for(x>>=1;x;x>>=1)T[x]=min(T[x<<1],T[x<<1|1]); } inline void update(int x) { if(!x)return; int l=c[x][0],r=c[x][1]; size[x]=size[l]+size[r]+1; mx[x]=min(mx[l],min(mx[r],num[x])); } void rotate(int x,int&k) { int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(y==k)k=x; else c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; update(y);update(x); } void splay(int x,int&k) { // cout<<"splay"<<x<<endl; while(x!=k) { int y=fa[x],z=fa[y]; if(y!=k) { if(c[z][1]==y^c[y][1]==x)rotate(x,k); else rotate(y,k); } rotate(x,k); } } void ins(int&x,int rk,int last,int w,int fid) { // cout<<"ins"<<x<<" "<<rk<<" "<<last<<" "<<w<<endl; if(!x){x=++cnt;fa[x]=last;s[x]=rk;num[x]=mx[x]=w;size[x]=1;id[x]=fid;return;} if(s[x]==rk)ins(c[x][(fid>id[x])],rk,x,w,fid); else if(s[x]>rk)ins(c[x][0],rk,x,w,fid); else ins(c[x][1],rk,x,w,fid); update(x); } void dfs(int x,int f) { fat[x]=f; for(int i=head[x];i;i=e2[i].next) if(e2[i].to!=f) {upx[e2[i].to]=e2[i].w;ins(rt[x],col[e2[i].to],0,e2[i].w,e2[i].to);dfs(e2[i].to,x);} } int find(int x,int rk,int fid) { if(s[x]==rk) {if(id[x]==fid)return x;return find(c[x][(fid>id[x])],rk,fid);} if(s[x]>rk)return find(c[x][0],rk,fid); else return find(c[x][1],rk,fid); } int get(int x,int rk) { // cout<<"get"<<x<<" "<<rk<<endl; int l=c[x][0],r=c[x][1]; if(size[l]+1==rk)return x; else if(size[l]+1<rk)return get(r,rk-size[l]-1); else return get(l,rk); } int del(int x) { // cout<<"del"<<x<<endl; int y=fat[x]; splay(find(rt[y],col[x],x),rt[y]); int w=num[rt[y]],rk=size[c[rt[y]][0]]; splay(get(rt[y],rk),c[rt[y]][0]); int pre=c[rt[y]][0]; c[pre][1]=c[rt[y]][1]; fa[c[rt[y]][1]]=pre;rt[y]=pre;update(pre); return w; } int ask(int x,int num,int pos) { // cout<<"ask"<<x<<" "<<num<<" "<<pos<<" "<<s[x]<<endl; if(!x)return 0;int q; if(s[x]==num)return (q=ask(c[x][pos],num,pos))>0?q:x; else if(s[x]<num)return ask(c[x][1],num,pos); else return ask(c[x][0],num,pos); } int query(int x) { int l=ask(rt[x],col[x],0),r=ask(rt[x],col[x],1); if(!l)return mx[rt[x]]; splay(l,rt[x]);if(r==l)return min(mx[c[l][0]],mx[c[l][1]]); splay(r,c[l][1]); return min(mx[c[rt[x]][0]],mx[c[c[rt[x]][1]][1]]); } int main() { freopen("grass.in","r",stdin); freopen("grass.out","w",stdout); n=read();m=read();read();qq=read();mx[0]=INF; for(register int i=1;i<=m;i++) e[i].from=read(),e[i].to=read(),e[i].w=read(); for(register int i=1;i<=n;++i)col[i]=read(); sort(e+1,e+m+1,cmp); mst();cnt=0; for(register int i=1;i<=n;++i)ins(rt[i],-INF,0,INF,0),ins(rt[i],INF,0,INF,0); dfs(1,0); for(register int i=1;i<=2*N+1;++i)T[i]=INF; for(register int i=1;i<=n;++i)renew(i,ans[i]=query(i)); int x,c; for(register int i=1;i<=qq;++i) { x=read(),c=read(); if(col[x]!=c) { if(x!=1){ins(rt[fat[x]],c,0,del(x),x); if(upx[x]<ans[fat[x]]||c==col[fat[x]])renew(fat[x],ans[fat[x]]=query(fat[x]));} col[x]=c;renew(x,ans[x]=query(x)); } printf("%d\n",T[1]); } return 0; }
C沙比提不想做
以上是关于usaco 2017 US Open的主要内容,如果未能解决你的问题,请参考以下文章
USACO 2016 US Open Contest, Gold解题报告
「USACO 2021 US Open Platinum」Balanced Subsets
USACO 2014 US Open Odometer /// 数位DP
USACO 2014 US Open Fair Photography /// 技巧