BZOJ 3648 寝室管理

【题解】

  GDOI2016 Day2T3

  如果给出的数据是一棵树那么皆大欢喜直接点分治就好了,用树状数组维护大于x的数的个数。如果是一棵基环树,我们先断掉环上的一条边,然后跑点分治;再加上经过这条边的方案数,其实就是从断掉的那条边的一个端点开始,把环遍历一遍,更新贡献的方式跟点分治过程中的差不多,具体看注释2333。

  

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rg register
 4 #define N 100010
 5 #define LL long long
 6 using namespace std;
 7 int n,m,k,tot,cnt,top,cir,root;
 8 int last[N],dep[N],bit[N<<1],siz[N],mxsiz[N],st[N],c[N];
 9 bool cut[N<<1],v[N];
10 LL ans=0;
11 struct edge{
12     int to,pre,dis;
13 }e[N<<1];
14 inline int read(){
15     int k=0,f=1; char c=getchar();
16     while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();
17     while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar();
18     return k*f;
19 }
20 inline void add(int x,int y){for(;x>0;x-=(x&-x))bit[x]+=y;}
21 inline int query(int x){
22     if(x<1) x=1; int ret=0;
23     for(;x<=n+cir;x+=(x&-x)) ret+=bit[x]; return ret;
24 }
25 void findcircle(int x,int fa){
26     if(cir) return;
27     if(v[x]){
28         c[cir=1]=x;
29         for(rg int i=top;st[i]!=x;c[++cir]=st[i--]);
30     }
31     v[x]=1; st[++top]=x;
32     for(rg int i=last[x],to;i;i=e[i].pre)
33         if((to=e[i].to)!=fa) findcircle(to,x);
34     v[x]=0; top--;
35 }
36 void getroot(int x,int fa){
37     siz[x]=1; mxsiz[x]=0;
38     for(rg int i=last[x],to;i;i=e[i].pre)if(!v[to=e[i].to]&&to!=fa&&!cut[i]){
39         getroot(to,x); siz[x]+=siz[to];
40         mxsiz[x]=max(mxsiz[x],siz[to]);
41     }
42     mxsiz[x]=max(mxsiz[x],cnt-mxsiz[x]);
43     if(!root||mxsiz[x]<mxsiz[root]) root=x;
44 }
45 void getdep(int x,int fa,int d){
46     st[++top]=d;
47     for(rg int i=last[x],to;i;i=e[i].pre)
48     if(!v[to=e[i].to]&&to!=fa&&!cut[i]) getdep(to,x,d+1);
49 }
50 void dfs(int x){
51     int l=0; v[x]=1;
52     for(rg int i=last[x],to;i;i=e[i].pre)
53     if(!v[to=e[i].to]&&!cut[i]){
54         l=top+1; getdep(to,x,1);
55         for(rg int j=l;j<=top;j++) ans+=query(k-st[j]-1);
56         for(rg int j=l;j<=top;j++) add(st[j],1);
57     }
58     ans+=query(k-1);
59     while(top) add(st[top--],-1);
60     for(rg int i=last[x],to;i;i=e[i].pre)
61     if(!v[to=e[i].to]&&!cut[i]&&siz[to]>=k){
62         cnt=siz[to]; root=0; getroot(to,x); dfs(root);
63     }
64 }
65 int main(){
66     n=read(); m=read(); k=read();
67     for(rg int i=1;i<=m;i++){
68         int u=read(),v=read();
69         e[++tot]=(edge){v,last[u]}; last[u]=tot;
70         e[++tot]=(edge){u,last[v]}; last[v]=tot;
71     }
72     if(m<n){
73         cnt=n; root=0; getroot(1,0); dfs(root);
74     }
75     else{
76         findcircle(1,0);
77         for(rg int i=last[c[1]],to;i;i=e[i].pre)if((to=e[i].to)==c[cir]){
78             cut[i]=1; break;
79         }
80         for(rg int i=last[c[cir]],to;i;i=e[i].pre)if((to=e[i].to)==c[1]){
81             cut[i]=1; break;
82         }
83         top=0; cnt=n; root=0; 
84         getroot(1,0); dfs(root);
85         top=0;
86         for(rg int i=1;i<=n;i++)v[i]=0;
87         for(rg int i=1;i<=cir+n;i++) bit[i]=0;
88         for(rg int i=1;i<=cir;i++) v[c[i]]=1;
89         for(rg int i=1;i<=cir;i++){
90             v[c[i]]=0;
91             getdep(c[i],0,0);
92             v[c[i]]=1;
93             for(rg int j=1;j<=top;j++)ans+=query(k-(cir-i+1)-st[j]);//cir-i+1是绕一圈到环上起点的距离 
94             while(top) add(st[top--]+i,1);//+i就是加上到环上起点的距离 
95         }
96     }
97     printf("%lld\n",ans);
98     return 0;
99 }
View Code

 

  

  

posted @ 2018-04-23 15:33  Driver_Lao  阅读(149)  评论(0编辑  收藏  举报