1. 程式人生 > >[Luogu3242][HNOI2015]接水果

[Luogu3242][HNOI2015]接水果

今天 mbo print lca ace 交換 最大 其中 pos

Luogu
我今天做兩道整體二分結果全都是BZOJ權限題???

sol

我們抓住“盤子的路徑是水果的路徑的子路徑”這個條件。
考慮每一個盤子路徑\((u,v)\),討論它可以作為哪些水果路徑的子路徑。
如果說\(u,v\)不是祖孫關系,那麽水果路徑的兩端點就必須分別在以\(u\)\(v\)為根的子樹中。即若一個水果路徑\((a,b)\)滿足\((u,v)\)是它的子路徑,則有\(dfn_u\le{dfn_a}\le{low_u},dfn_v\le{dfn_b}\le{low_v}\)(請自行判斷是否交換\(u,v\)\(a,b\)的順序)
其中\(low_u\)是以\(u\)為根的子樹中的最大\(dfn\)

序。
如果\(u,v\)是祖孫關系,假設\(u\)是祖先,那麽據路徑的一端一定要在\(v\)的子樹裏,另一端的位置,要保證不在\(w\)的子樹裏,其中\(w\)\(u\)的直接兒子也是\(v\)的祖先(當然只有那一個啦)。
所以就是要滿足\(1\le{dfn_a}<dfn_w\mbox{或}low_u<dfn_a\le{n},dfn_v\le{dfn_b}\le{low_v}\)
發現這個類似一個二維的矩形呀,所以就可以做一個掃描線,樹狀數組統計答案就可以了。
把所有盤子視為一個或是兩個矩形,按權值大小排序,每次二分一個位置判斷某一點(一個水果)是否已經被\(k\)個矩形覆蓋,然後向下遞歸即可。
依舊是整體二分的板子

code

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 40005;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'
&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return w?x:-x; } struct edge{int to,next;}a[N<<1]; struct plate{ int x1,x2,y1,y2,v; bool operator < (const plate &b) const {return v<b.v;} }p[N<<1]; struct fruit{ int x,y,k,id; bool operator < (const fruit &b) const {return x<b.x;} }q[N],q1[N],q2[N]; struct node{ int x,y,v; bool operator < (const node &b) const {return x<b.x;} }zsy[N<<2]; int n,P,Q,head[N],cnt,fa[N],dep[N],sz[N],son[N],top[N],dfn[N],low[N],tot,c[N],ans[N]; void dfs1(int u,int f) { fa[u]=f;dep[u]=dep[f]+1;sz[u]=1; for (int e=head[u];e;e=a[e].next) { int v=a[e].to;if (v==f) continue; dfs1(v,u); sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v; } } void dfs2(int u,int up) { top[u]=up;dfn[u]=++cnt; if (son[u]) dfs2(son[u],up); for (int e=head[u];e;e=a[e].next) if (a[e].to!=fa[u]&&a[e].to!=son[u]) dfs2(a[e].to,a[e].to); low[u]=cnt; } int getlca(int u,int v) { while (top[u]^top[v]) { if (dep[top[u]]<dep[top[v]]) swap(u,v); u=fa[top[u]]; } return dep[u]<dep[v]?u:v; } int getson(int u,int v) { int gg; while (top[u]^top[v]) gg=top[v],v=fa[top[v]]; return u==v?gg:son[u]; } void modify(int k,int v){while (k<=n) c[k]+=v,k+=k&-k;} int query(int k){int s=0;while (k) s+=c[k],k-=k&-k;return s;} void solve(int L,int R,int l,int r)//LR詢問(水果)區間 lr二分答案區間 { if (L>R) return; if (l==r) { for (int i=L;i<=R;i++) ans[q[i].id]=p[l].v; return; } int mid=l+r>>1,top=0,pos=0,t1=0,t2=0,temp; for (int i=l;i<=mid;i++) { zsy[++top]=(node){p[i].x1,p[i].y1,1}; zsy[++top]=(node){p[i].x1,p[i].y2+1,-1}; zsy[++top]=(node){p[i].x2+1,p[i].y1,-1}; zsy[++top]=(node){p[i].x2+1,p[i].y2+1,1}; } sort(zsy+1,zsy+top+1); for (int i=L;i<=R;i++) { while (pos<top&&zsy[pos+1].x<=q[i].x) pos++,modify(zsy[pos].y,zsy[pos].v); temp=query(q[i].y); if (q[i].k<=temp) q1[++t1]=q[i]; else q[i].k-=temp,q2[++t2]=q[i]; } while (pos<top) pos++,modify(zsy[pos].y,zsy[pos].v);//記得這裏要清空樹狀數組 for (int i=L,j=1;j<=t1;i++,j++) q[i]=q1[j]; for (int i=L+t1,j=1;j<=t2;i++,j++) q[i]=q2[j]; solve(L,L+t1-1,l,mid);solve(L+t1,R,mid+1,r); } int main() { n=gi();P=gi();Q=gi(); for (int i=1,u,v;i<n;i++) { u=gi();v=gi(); a[++cnt]=(edge){v,head[u]};head[u]=cnt; a[++cnt]=(edge){u,head[v]};head[v]=cnt; } dfs1(1,0);cnt=0;dfs2(1,1); for (int i=1,u,v,w,gg;i<=P;i++) { u=gi();v=gi();w=gi(); if (dfn[u]>dfn[v]) swap(u,v); if (getlca(u,v)==u) { gg=getson(u,v); if (dfn[gg]>1) p[++tot]=(plate){1,dfn[gg]-1,dfn[v],low[v],w}; if (low[gg]<n) p[++tot]=(plate){dfn[v],low[v],low[gg]+1,n,w}; } else p[++tot]=(plate){dfn[u],low[u],dfn[v],low[v],w}; } sort(p+1,p+tot+1); for (int i=1,u,v,k;i<=Q;i++) { u=gi();v=gi();k=gi(); if (dfn[u]>dfn[v]) swap(u,v); q[i]=(fruit){dfn[u],dfn[v],k,i}; } sort(q+1,q+Q+1); solve(1,Q,1,tot); for (int i=1;i<=Q;i++) printf("%d\n",ans[i]); return 0; }

[Luogu3242][HNOI2015]接水果