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 }
View Code

  题目大意:给定一棵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 }
View Code

  题目大意:给你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 }
View Code

  题目大意:给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,十分优秀的做法。

posted @ 2017-02-10 23:46 oyzx~ 阅读(...) 评论(...) 编辑 收藏