[HNOI2015]接水果(整体二分)

匿名 (未验证) 提交于 2019-12-03 00:26:01

其实我真的不想写这道题,然而迫于有一个技巧,不得不写一写(主要是为了把技巧放进博客)
保证一个月内不写代码超过100行的。

Description

风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。
由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更
加难的版本。首先有一个地图,是一棵由 n 个顶点、n1 条边组成的树(例如图 1
给出的树包含 8 个顶点、7 条边)。这颗树上有P 个盘子,每个盘子实际上是一条
路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i
盘子就是顶点ai到顶点bi的路径(由于是树,所以从aibi的路径是唯一的),权值为ci。接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第i 个水果是从顶点 ui 到顶点vi的路径。幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与从ba的路径是同一条路径。当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 ki 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

Input

第一行三个数 nPQ,表示树的大小和盘子的个数和水果的个数。

接下来n1 行,每行两个数 ab,表示树上的ab 之间有一条边。树中顶点按1到 n标号。 接下来 P 行,每行三个数 abc,表示路径为 ab、权值为 c 的盘子,其中0c109a不等于b
接下来Q行,每行三个数 uvk,表示路径为 uv的水果,其中 u不等于v,你需要选择第 k小的盘子,
k 小一定存在。

Output

对于每个果子,输出一行表示选择的盘子的权值。

Sample Input

10 10 10  1 2  2 3  3 4  4 5  5 6  6 7  7 8  8 9  9 10  3 2 217394434  10 7 13022269  6 7 283254485  6 8 333042360  4 6 442139372  8 3 225045590  10 4 922205209  10 8 808296330  9 2 486331361  4 9 551176338  1 8 5  3 8 3  3 8 4  1 8 3  4 8 1  2 3 1  2 3 1  2 3 1  2 4 1  1 4 1  

Sample Output

442139372  333042360  442139372  283254485  283254485  217394434  217394434  217394434  217394434  217394434  

HINT

N,P,Q<=40000












这道题首先需要解决的问题是如何判断水果是否出现在盘子上。而且,我们对于一个盘子,要很快地求出有多少个水果经过它。

搬运
首先得到每个节点的dfs序和以这个节点为根的子树中dfs序最大值,分别记作dfn[u]last[u]

显然,A路径覆盖B路径当且仅当B路径的两个端点都在A路径上

对于每一个盘子(u,v)(假设dfn[u]<dfn[v]),有以下两种情况

1.lca(u,v)!=u时,如图

(虚线表示有一条路径,实线表示一条边,下同)

显然这个盘子能接到的水果(a,b)(假设dfn[a]<dfn[b],下同)满足dfn[u]<=dfn[a]<=last[u] && dfn[v]<=dfn[b]<=last[v]

2.当lca(u,v)==u时,如图

wΪu(u,v)这条路径上的儿子节点

则这个盘子能接到的水果(a,b)满足((1<=a<=dfn[w]1)||(last[w]+1<=a<=n))&& (dfn[v]<=b<=last[v])

我们可以将水果(a,b)看成平面上的一个点(dfn[a],dfn[b]),将盘子看成一个或两个矩形
那么问题就转化为对于平面上的一个点,求覆盖它的第k小的矩形
这是一个整体二分的经典题,用扫描线+树状数组搞定
我们先将矩形按权值从小到大排序
然后对于一个点,如果[l,mid]中能覆盖这个点的矩形数不小于k,则说明答案在[l,mid]中
否则在[mid+1,r],同时k减去覆盖的矩形数

大佬说的很清楚啦~
发现整体二分似乎并不能先把矩阵拆成扫描线,要在二分中间拆成扫描线。不知道是不是姿势不对?似乎两个边界会被分到两边,对答案有问题?
我就只好把矩形二分,然后要算的时候就直接拆成扫描线。
唉,怕不是写假了,别人都只写了100行多一点。不知道是树剖长了还是整体二分长了。怕不是都长了。
反正都180行了,根本不想卡常,sort算了。-_-
code:

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define lowbit(x) ((x)&(-x)) using namespace std; int key; struct lxy{     int next,to; }eg[80005]; struct lxy2{     int xl,xr,yl,yr,k;     bool operator < (const lxy2 &QAQ)const{       if(key==0) return k<QAQ.k;     } }d[200005]; struct lxy3{     int l,r,k,tim,ans;     bool operator < (const lxy3 &QAQ)const{       if(key==0) return tim<QAQ.tim;       if(key==1) return l<QAQ.l;      } }f[40005],tax[40005]; struct lxy4{     int x,yl,yr,type;     bool operator < (const lxy4 &QAQ)const{       if(key==1) return x<QAQ.x;     } }s[200005]; int n,p,q,x,y,k,cnt,tim,kl; int head[40005],tp[40005],fa[40005],dep[40005],size[40005],wson[40005],dfn[40005],last[40005]; bool vis[40005]; int b[40005];  void add(int op,int ed) {     eg[++cnt].next=head[op];     eg[cnt].to=ed;     head[op]=cnt; }  void dfs1(int u,int dp) {     vis[u]=1;dep[u]=dp;size[u]=1;dfn[u]=++tim;     int t=0;     for(int i=head[u];i!=-1;i=eg[i].next)       if(vis[eg[i].to]==0)       {          fa[eg[i].to]=u;          dfs1(eg[i].to,dp+1);          size[u]+=size[eg[i].to];          if(size[eg[i].to]>t){             t=size[eg[i].to];             wson[u]=eg[i].to;          }       }       vis[u]=0; }  int dfs2(int u,int las) {     tp[u]=las;vis[u]=1;last[u]=dfn[u];     if(wson[u]!=0) last[u]=max(last[u],dfs2(wson[u],las));     for(int i=head[u];i!=-1;i=eg[i].next)       if(vis[eg[i].to]==0&&eg[i].to!=wson[u])          last[u]=max(last[u],dfs2(eg[i].to,eg[i].to));     vis[u]=0;     return last[u]; }  bool lca(int w,int v) {     int fro=v;     while(tp[w]!=tp[v]&&v!=0){         fro=tp[v],v=fa[tp[v]];     }     if(v==0||dep[v]<dep[w]) return true;     if(v==w) kl=fro;     else kl=wson[w];     return false; }  void modify(int u,int r){     while(u<=n){         b[u]+=r;         u+=lowbit(u);     } }  int query(int u){     int ret=0;     while(u>0){         ret+=b[u];         u-=lowbit(u);     }     return ret; }  void CDQ(int ml,int mr,int ql,int qr) {     if(ml==mr){         for(int i=ql;i<=qr;i++)           f[i].ans=d[ml].k;         return;     }     int mid=(ml+mr)>>1,cnt_=0;     for(int i=ml;i<=mid;i++){         s[++cnt_].x=d[i].xl;s[cnt_].yl=d[i].yl;s[cnt_].yr=d[i].yr;s[cnt_].type=1;         s[++cnt_].x=d[i].xr;s[cnt_].yl=d[i].yl;s[cnt_].yr=d[i].yr;s[cnt_].type=-1;     }     int t1=1,t2=ql,l=ql-1,r=qr+1;     key=1,sort(s+1,s+cnt_+1),sort(f+ql,f+qr+1);     while(t2<=qr){         if(t1<=cnt_&&s[t1].x<=f[t2].l){           modify(s[t1].yl,s[t1].type);           modify(s[t1].yr+1,-s[t1].type);           t1++;         }         else{             int z=query(f[t2].r);             if(z<f[t2].k) f[t2].k-=z,tax[--r]=f[t2];             else tax[++l]=f[t2];             t2++;         }     }     for(int i=1;i<t1;i++){         modify(s[i].yl,-s[i].type);         modify(s[i].yr+1,s[i].type);     }     for(int i=ql;i<=qr;i++)       f[i]=tax[i];     CDQ(ml,mid,ql,l);     CDQ(mid+1,mr,r,qr); }  int main() {     memset(head,-1,sizeof(head));     scanf("%d%d%d",&n,&p,&q);     for(int i=1;i<n;i++)     {         scanf("%d%d",&x,&y);         add(x,y);add(y,x);     }     dfs1(1,1);dfs2(1,1);     cnt=0;     for(int i=1;i<=p;i++){       scanf("%d%d%d",&x,&y,&k);       if(dfn[x]>dfn[y]) swap(x,y);       if(lca(x,y)){         d[++cnt].xl=dfn[x];d[cnt].xr=last[x]+1;d[cnt].yl=dfn[y];d[cnt].yr=last[y];d[cnt].k=k;       }       else{         d[++cnt].xl=1;d[cnt].xr=dfn[kl];d[cnt].yl=dfn[y];d[cnt].yr=last[y];d[cnt].k=k;         d[++cnt].xl=dfn[y];d[cnt].xr=last[y]+1;d[cnt].yl=last[kl]+1;d[cnt].yr=n;d[cnt].k=k;       }     }     for(int i=1;i<=q;i++){       scanf("%d%d%d",&x,&y,&f[i].k);       f[i].tim=i;       f[i].l=min(dfn[x],dfn[y]);       f[i].r=max(dfn[x],dfn[y]);     }     key=0,sort(d+1,d+1+cnt);     CDQ(1,cnt,1,q);     key=0,sort(f+1,f+q+1);     for(int i=1;i<=q;i++)       printf("%d\n",f[i].ans); }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!