【XSY3350】svisor - 点分治+虚树dp

题目来源:NOI2019模拟测试赛(九)

题意:

吐槽:

第一眼看到题觉得这不是震波的完全弱化版吗……然后开开心心的码了个点分治

码到一半突然发现看错题了……心态崩了于是就弃疗手玩提答去了

于是就快乐垫底了

最后发现这是个最毒瘤的题……改题写+调了一天,代码长度再次进入前五排行榜

题解:

(明明是在线做法为什么不强制在线呢)

由于是询问树上某些关键点的信息,且$\sum k$比较小,所以考虑建出虚树处理询问;

如图,对于虚树上一个不是关键点的点$u$,显然他的最大监视半径就是$max\{r_v-dis_{u,v}|v是u的子节点\}$;

这个可以通过逆拓扑序在虚树上一遍DP求出来;

由于虚树的点数是$O(k)$的,所以可以直接用点分治预处理离每个点距离小于等于$r$的点数量,然后$O(logn)$处理虚树上所有点的询问;

但是这样做显然会有点被重复计算,如图,阴影部分的点就被计算了两次(图中是一条链,实际上可能还有其它分支也被重复计算了);

考虑被重复计算的部分有什么性质,容易发现它实际上就是距离$u$和$v$的监视半径重叠部分中心不超过$\frac{r_u+r_v-dis_{u,v}}{2}$的点集,显然这也可以当成类似的询问用点分治处理;

对虚树上每一对有重叠的父子都类似处理一遍,就可以减去所有重叠部分的额外影响,因此不用额外考虑被覆盖了三次四次甚至以上的点;

由于虚树的边数=点数-1,所以这一部分的时间复杂度显然是对的;

但是这样还有一个小问题:如图,如果重叠部分边长度为奇数,那么是找不到中心点的;

实际上这时中心点在一条边上,所以可以拆边,把树上每条原本的边都看成一个点,就可以解决了;

至此这道题终于做完了……具体实现的时候并不用把虚树真正建出来,只记录每个点的父节点和拓扑序即可;

总的时间复杂度$O((n+\sum k)logn)$,常数很大。

写的时候细节超多……外面的点分治要记一万个信息……轻松喜提200行+

代码:

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<vector>
  6 #include<cmath>
  7 #include<queue>
  8 #include<stack>
  9 #define inf 2147483647
 10 #define eps 1e-9
 11 using namespace std;
 12 typedef long long ll;
 13 typedef double db;
 14 struct edge{
 15     int v,next;
 16 }a[200001];
 17 int n,m,u,v,K,S,rt,mxd,ans,tot=0,tim=0,fkfa[100001],dfn[100001],nmd[100001],head[100001],dps[100001],md1[100001],*s1[100001],md2[100001],*s2[100001],ddp[100001],dfdep[100001],dfrt[100001][20],dfds[100001][20],k[100001],r[100001],siz[100001],mx[100001],dep[100001],fa[100001][17];
 18 bool used[100001],isk[100001];
 19 stack<int>st;
 20 vector<int>vec;
 21 bool cmp(int a,int b){
 22     return dfn[a]<dfn[b];
 23 }
 24 void add(int u,int v){
 25     a[++tot].v=v;
 26     a[tot].next=head[u];
 27     head[u]=tot;
 28 }
 29 void dfs(int u,int ff,int dpt){
 30     dep[u]=dpt;
 31     dfn[u]=++tim;
 32     nmd[tim]=u;
 33     fa[u][0]=ff;
 34     for(int i=1;i<=16;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
 35     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 36         int v=a[tmp].v;
 37         if(v!=ff){
 38             dfs(v,u,dpt+1);
 39         }
 40     }
 41 }
 42 int lca(int u,int v){
 43     if(dep[u]<dep[v])swap(u,v);
 44     int l=dep[u]-dep[v];
 45     for(int i=16;i>=0;i--){
 46         if((1<<i)&l){
 47             u=fa[u][i];
 48         }
 49     }
 50     if(u==v)return u;
 51     for(int i=16;i>=0;i--){
 52         if(fa[u][i]!=fa[v][i]){
 53             u=fa[u][i],v=fa[v][i];
 54         }
 55     }
 56     return fa[u][0];
 57 }
 58 int getfa(int u,int l){
 59     for(int i=16;i>=0;i--){
 60         if((1<<i)&l){
 61             u=fa[u][i];
 62         }
 63     }
 64     return u;
 65 }
 66 void dfsrt(int u,int fa){
 67     siz[u]=1;
 68     mx[u]=0;
 69     mxd=max(mxd,ddp[u]);
 70     if(u<=n)dps[ddp[u]]++;
 71     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 72         int v=a[tmp].v;
 73         if(!used[v]&&v!=fa){
 74             dfsrt(v,u);
 75             siz[u]+=siz[v];
 76             mx[u]=max(mx[u],siz[v]);
 77         }
 78     }
 79     mx[u]=max(mx[u],S-siz[u]);
 80     if(mx[u]<mx[rt])rt=u;
 81 }
 82 void dfsdep(int u,int fa,int ls,int dpt){
 83     siz[u]=1;
 84     ddp[u]=dpt;
 85     dfdep[u]++;
 86     dfrt[u][dfdep[u]]=ls;
 87     dfds[u][dfdep[u]]=dpt;
 88     mxd=max(mxd,dpt);
 89     if(u<=n)dps[dpt]++;
 90     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
 91         int v=a[tmp].v;
 92         if(!used[v]&&v!=fa){
 93             dfsdep(v,u,ls,dpt+1);
 94             siz[u]+=siz[v];
 95         }
 96     }
 97 }
 98 void divide(int u){
 99     used[u]=true;
100     ddp[u]=mxd=0;
101     dfsdep(u,0,u,0);
102     md1[u]=mxd;
103     s1[u]=new int[mxd+1];
104     for(int i=0;i<=mxd;i++){
105         s1[u][i]=dps[i];
106         if(i)s1[u][i]+=s1[u][i-1];
107         dps[i]=0;
108     }
109     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
110         int v=a[tmp].v;
111         if(!used[v]){
112             mxd=rt=0;
113             S=siz[v];
114             dfsrt(v,u);
115             md2[rt]=mxd;
116             s2[rt]=new int[mxd+1];
117             for(int i=0;i<=mxd;i++){
118                 s2[rt][i]=dps[i];
119                 if(i)s2[rt][i]+=s2[rt][i-1];
120                 dps[i]=0;
121             }
122             divide(rt);
123         }
124     }
125 }
126 void buildfaketree(){
127     while(!st.empty())st.pop();
128     vec.clear();
129     sort(k+1,k+K+1,cmp);
130     st.push(k[1]);
131     for(int i=2;i<=K;i++){
132         int z=lca(k[i],st.top());
133         if(!isk[z])r[z]=-1;
134         while(!st.empty()&&dep[z]<dep[st.top()]){
135             int x=st.top();
136             st.pop();
137             vec.push_back(x);
138             if(!st.empty()&&dep[z]<dep[st.top()])fkfa[x]=st.top();
139             else fkfa[x]=z;
140         }
141         if(st.empty()||dep[z]>dep[st.top()])st.push(z);
142         if(k[i]!=z)st.push(k[i]);
143     }
144     while(!st.empty()){
145         int x=st.top();
146         st.pop();
147         vec.push_back(x);
148         if(!st.empty())fkfa[x]=st.top();
149         else fkfa[x]=0;
150     }
151 }
152 int getci(int u,int r){
153     int nw,d1,d2,ret=0;
154     for(int i=dfdep[u];i;i--){
155         nw=dfrt[u][i];
156         d1=dfds[u][i];
157         d2=dfds[u][i-1];
158         if(d1<=r)ret+=s1[nw][min(r-d1,md1[nw])];
159         if(i>1&&d2<=r)ret-=s2[nw][min(r-d2,md2[nw])];
160     }
161     return ret;
162 }
163 void getans(){
164     ans=0;
165     int u,ft,ds,mid,len=vec.size();
166     for(int i=0;i<len;i++){
167         u=vec[i];
168         r[fkfa[u]]=max(r[fkfa[u]],r[u]-dep[u]+dep[fkfa[u]]);
169     }
170     for(int i=len-1;i>=0;i--){
171         u=vec[i];
172         r[u]=max(r[u],r[fkfa[u]]-dep[u]+dep[fkfa[u]]);
173     }
174     for(int i=0;i<len;i++){
175         u=vec[i];
176         ans+=getci(u,r[u]);
177     }
178     for(int i=0;i<len-1;i++){
179         u=vec[i];
180         ft=fkfa[u];
181         ds=dep[u]-dep[ft];
182         if(r[u]+r[ft]>=ds){
183             mid=getfa(u,(r[u]-r[ft]+ds)/2);
184             ans-=getci(mid,r[u]-(r[u]-r[ft]+ds)/2);
185         }
186     }
187 }
188 void pt(int *s){
189     for(int i=1;i<=n*2-1;i++)printf("%d ",s[i]);
190     puts("");
191 }
192 int main(){
193     memset(head,-1,sizeof(head));
194     scanf("%d",&n);
195     for(int i=1;i<n;i++){
196         scanf("%d%d",&u,&v);
197         add(u,n+i);
198         add(n+i,u);
199         add(v,n+i);
200         add(n+i,v);
201     }
202     dfs(1,0,0);
203     S=n*2-1;
204     mx[rt]=6666666;
205     dfsrt(1,-1);
206     memset(dps,0,sizeof(dps));
207     divide(rt);
208     scanf("%d",&m);
209     while(m--){
210         scanf("%d",&K);
211         r[0]=-1;
212         for(int i=1;i<=K;i++){
213             scanf("%d",&k[i]);
214             scanf("%d",&r[k[i]]);
215             r[k[i]]*=2;
216             isk[k[i]]=true;
217         }
218         buildfaketree();
219         for(int i=1;i<=K;i++)isk[k[i]]=false;
220         getans();
221         printf("%d\n",ans);
222     }
223     return 0;
224 } 
posted @ 2018-12-25 09:23  DCDCBigBig  阅读(355)  评论(0编辑  收藏  举报