Codeforces Round #395 (Div. 1)
比赛链接:http://codeforces.com/contest/763
A题:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 const int N=100005; 8 int n,tot,now[N],prep[N<<1],son[N<<1],c[N],a[N]; 9 void read(int &x){ 10 x=0; int f=1; char ch; 11 for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1; 12 for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; x*=f; 13 } 14 void add(int u,int v){tot++,prep[tot]=now[u],now[u]=tot,son[tot]=v;} 15 int main(){ 16 read(n); 17 for (int u,v,i=1;i<n;i++){ 18 read(u),read(v); 19 add(u,v),add(v,u); 20 } 21 for (int i=1;i<=n;i++) read(c[i]); 22 for (int i=1;i<=n;i++) 23 for (int j=now[i],k=son[j];j;j=prep[j],k=son[j]){ 24 if (i<k&&c[i]!=c[k]) a[i]++,a[k]++; 25 } 26 int cnt=0,id; 27 for (int i=1;i<=n;i++) if (a[i]>=2) cnt++,id=i; 28 if (cnt>1) puts("NO"); 29 else if (cnt==1){ 30 for (int i=now[id],j=son[i];i;i=prep[i],j=son[i]) if (c[id]!=c[j]) a[j]--; 31 for (int i=1;i<=n;i++) if (i!=id&&a[i]){puts("NO");return 0;} 32 puts("YES"); printf("%d\n",id); 33 }else{ 34 cnt=0; 35 for (int i=1;i<=n;i++) if (a[i]) cnt++,id=i; 36 if (cnt>2) puts("NO"); 37 else if (cnt==2) printf("YES\n%d\n",id); 38 else printf("YES\n%d\n",1); 39 } 40 return 0; 41 }
题目大意:给定一棵n个节点的树,每个点有个颜色,询问是否能以某个点为树根时,所有节点(树根除外)的子树中的颜色一致,有则输出YES,且输出树根的编号,否则输出NO; n,color[i]<=10^5;
做法:很容易yy出一种做法,就是记录每个点周围与它颜色不一致的节点个数,显然我们把这个值最大的那个点作为树根时最有可能合法嘛,那么找出树根,大力check一下即可。
B题:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 int main(){ 8 int n,x1,y1,x2,y2; 9 cin>>n; puts("YES"); 10 while (n--){ 11 cin>>x1>>y1>>x2>>y2; 12 cout<<(x1&1)*2+(y1&1)+1<<endl; 13 } 14 return 0; 15 }
题目大意:给你n(n<=500000)个矩形,边长为奇数,矩阵不能相交,但是能相邻(也就是边有交集,且len>0),我们要用4种颜色去给矩形染色,要求相邻的矩形颜色不同,存在方案,则输出YES,并且要输出每个矩形的颜色,1<=color<=4,否则输出NO;
做法:根据数学中的四色定理可知,一定存在某种方案,那么,我们该怎么构造一种方案,这题有个重要的性质,边长为奇数,奇数有什么性质?奇数+奇数=偶数,偶数+奇数=奇数,也就是说一个数加上一个奇数,那么这个数的奇偶性就会发生改变,一种神奇的思路:取矩形的左上端点(x,y)来分类,
1.x为奇数,y为偶数,染为颜色1;
2.x为奇数,y为奇数,染为颜色2;
3.x为偶数,y为偶数,染为颜色3;
4.x为偶数,y为奇数,染为颜色4;
证明:如果两个矩形相邻,设这2个矩形的左上端点为(x1,y1),(x2,y2),由于矩形的边长为奇数,(x1,x2奇偶性不同),(y1,y2奇偶性不同)这两个条件至少成立一个,此时他们一定具有不同的颜色,得证。
E题:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <vector> 7 #define PI pair<int,int> 8 #define mp(a,b) make_pair(a,b) 9 using namespace std; 10 const int N=100005,M=N*6,K=N*5; 11 int n,k,m,q,wi[K][2],fa[M],ans[N],son[M][2],smax[M],val[M],smax_id[M],tree[N]; 12 vector <PI> vec[N],a[N]; 13 void read(int &x){ 14 x=0; int f=1; char ch; 15 for (ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') f=-1; 16 for (;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; x*=f; 17 } 18 struct T{ 19 int lowbit(int x){return x&(-x);} 20 void insert(int x,int y){for (int i=x;i<=n;i+=lowbit(i)) tree[i]+=y;} 21 int query(int x){ 22 int sum=0; 23 for (int i=x;i;i-=lowbit(i)) sum+=tree[i]; 24 return sum; 25 } 26 }t; 27 struct link_cut_tree{ 28 bool rev[M]; 29 int which(int x){return son[fa[x]][1]==x;} 30 bool isroot(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;} 31 void update(int x){ 32 smax[x]=val[x],smax_id[x]=x; 33 if (son[x][0]&&smax[son[x][0]]>smax[x]) smax[x]=smax[son[x][0]],smax_id[x]=smax_id[son[x][0]]; 34 if (son[x][1]&&smax[son[x][1]]>smax[x]) smax[x]=smax[son[x][1]],smax_id[x]=smax_id[son[x][1]]; 35 } 36 void pushdown(int x){ 37 if (!rev[x]) return; 38 rev[x]^=1,swap(son[x][0],son[x][1]); 39 if (son[x][0]) rev[son[x][0]]^=1; 40 if (son[x][1]) rev[son[x][1]]^=1; 41 } 42 void relax(int x){ 43 if (!isroot(x)) relax(fa[x]); 44 pushdown(x); 45 } 46 void rotate(int x){ 47 int y=fa[x],d=which(x),dd=which(y); 48 if (!isroot(y)) son[fa[y]][dd]=x; fa[x]=fa[y]; 49 fa[son[x][d^1]]=y,son[y][d]=son[x][d^1]; 50 fa[y]=x,son[x][d^1]=y,update(y); 51 } 52 void splay(int x){ 53 relax(x); 54 while (!isroot(x)){ 55 if (isroot(fa[x])) rotate(x); 56 else if (which(x)==which(fa[x])) rotate(fa[x]),rotate(x); 57 else rotate(x),rotate(x); 58 } 59 update(x); 60 } 61 void access(int x){for (int p=0;x;x=fa[x]) splay(x),son[x][1]=p,p=x,update(x);} 62 void make_root(int x){access(x),splay(x),rev[x]^=1;} 63 void link(int u,int v){make_root(u),fa[u]=v;} 64 void cut(int u,int v){make_root(u),access(v),splay(v),son[v][0]=fa[u]=0,update(v);} 65 int find_root(int x){ 66 access(x),splay(x); 67 while (1){ 68 pushdown(x); 69 if (son[x][0]) x=son[x][0]; 70 else return x; 71 } 72 } 73 void split(int u,int v){make_root(u),access(v),splay(v);} 74 int query(int u,int v){split(u,v);return smax_id[v];} 75 }lct; 76 int main(){ 77 read(n),read(k),read(m); 78 for (int u,v,i=1;i<=m;i++){ 79 read(u),read(v); if (u>v) swap(u,v); 80 vec[u].push_back(mp(v,i)),wi[i][0]=u,wi[i][1]=v; 81 } 82 for (int i=1;i<=n;i++) val[i]=0,lct.update(i); 83 for (int i=1;i<=m;i++) val[n+i]=wi[i][1],lct.update(n+i); 84 read(q); 85 for (int l,r,i=1;i<=q;i++){ 86 read(l),read(r); 87 a[l].push_back(mp(r,i)); 88 } 89 for (int i=n;i>=1;i--){ 90 for (int u,v,j=0;j<vec[i].size();j++){ 91 int x=vec[i][j].second; 92 u=wi[x][0],v=wi[x][1]; 93 if (lct.find_root(u)!=lct.find_root(v)) lct.link(u,x+n),lct.link(v,x+n),t.insert(v,1); 94 else{ 95 int y=lct.query(u,v)-n; 96 if (wi[y][1]>wi[x][1]){ 97 t.insert(wi[y][1],-1); 98 t.insert(wi[x][1],1); 99 lct.cut(y+n,wi[y][0]),lct.cut(y+n,wi[y][1]); 100 lct.link(x+n,u),lct.link(x+n,v); 101 } 102 } 103 } 104 for (int j=0;j<(int)a[i].size();j++){ 105 int x=a[i][j].first; 106 ans[a[i][j].second]=x-i+1-t.query(x); 107 } 108 } 109 for (int i=1;i<=q;i++) printf("%d\n",ans[i]); 110 return 0; 111 }
题目大意:给n个点,m条边,然后q个询问,每次询问[l,r]区间中所有点带上其两两点之间的边组成的图的连通分量个数,n,q<=10^5,m<=500000;
做法:
做法1:分治+可持久化并查集,复杂度nlogn^3,这题过不了。
做法2:离线处理+lct维护mst,这种做法很神奇,是我看题解的时候学到的,大致做法如下:将所有询问按照左端点排序,然后按左端点从大到小考虑,做到所有左端点为l的询问时,我们将所有边(u,v),满足u>=l,u<v的边,令其权值为v,即可参与mst的构建,这个过程去lct维护,对于一个询问l,r,答案就是r-l+1-此时mst中权值<=r的边的条数,这个用树状数组维护即可,总复杂度nlogn,十分优秀的做法。