bzoj3545 && bzoj3551 Peaks(离线版&&在线版)
题目给n点m边的无向图,有点权和边权
每次询问求点v在经过路径上的边都不超过w的情况下,能到达的第k大的点的权值
首先离线版比较容易想到,属于我现在能码出来的最难的码农题之一吧T T
这道题思路是这样的
1、对于边权的限制条件,可以先想到做一棵最小生成树
2、对于第k大这种询问,可以建权值线段树,但是山的高度太大到1e9,所以我们还要先离散化到1e6的水平才能用线段树
3、显然不能对每个询问w都建线段树,因此要用到主席树。具体做法是将询问和边权都排序(详情看代码),给每个点建一棵线段树,然后边建mst边回答询问,每次合并两个连通块的时候,要将两个连通块的线段树合并起来,线段树支持区间加减的优势就出来了
离线的做法是这样的,代码也挺好写
然后再来考虑在线怎么做= =
题解的做法很神,很难想到
同样是建最小生成树,不过做了变形
比如每次连边有 x=find(e[i].from), y=find(e[i].to);
这时候新建一个节点z,点权是边权
然后fa[x]=fa[y]=z,即新建的节点是此连通块的根
这样做有什么用呢。。好处就是建完树后,所有的MST的节点都在叶子节点,上边的全是由边转化的点
而且由kruskal的过程可知,越晚加入的边,边权越大,因此在新树中,除了叶子节点,越往上点的权值越大
这一点就可以被每次询问的边权不超过w所用,此时w只要从询问的v开始,倍增往上找到第一个边权大于它的点
一开始还要求出此树的dfs序:叶子节点仅保留一次,非叶子节点(即边的节点)保留low和high
然后建主席树,仅叶子节点要修改权值线段树,否则不进行修改,将上棵树的根复制过来
询问的时候,找到第一个大于w的点v,利用low[v]和high[v]这两个根的线段树,减一减就是v在不超过w的情况下能走到的点的权值
然后找就是了
离线:
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 100002; 6 struct node{ 7 int u,v,w,id; 8 bool q; 9 }e[maxn*10]; 10 int n,m,T,N,ls[maxn*50],rs[maxn*50],sum[maxn*50],tot,h[maxn],ori[maxn],a[maxn],root[maxn],fa[maxn],ans[maxn*5]; 11 12 void read(int &x){ 13 x=0; int f=1; char c=getchar(); 14 while (c<'0' || c>'9'){if (c=='-') f=-1; c=getchar();} 15 while (c>='0' && c<='9') x=x*10+c-48,c=getchar(); 16 x*=f; 17 } 18 19 bool operator<(node a, node b){ 20 return a.w==b.w?a.q<b.q:a.w<b.w; 21 } 22 23 int find(int x){ 24 return fa[x]==x?x:fa[x]=find(fa[x]); 25 } 26 27 void insert(int &x, int l, int r, int pos){ 28 if (!x) x=++tot; 29 if (l==r){ 30 sum[x]=1; 31 return; 32 } 33 int mid=l+r>>1; 34 if (pos<=mid) insert(ls[x],l,mid,pos); 35 else insert(rs[x],mid+1,r,pos); 36 sum[x]=sum[ls[x]]+sum[rs[x]]; 37 } 38 39 int merge(int x, int y){ 40 if (!x || !y) return x+y; 41 if (!ls[x] && !rs[x]){ 42 sum[x]+=sum[y]; 43 return x; 44 } 45 ls[x]=merge(ls[x],ls[y]); 46 rs[x]=merge(rs[x],rs[y]); 47 sum[x]=sum[ls[x]]+sum[rs[x]]; 48 return x; 49 } 50 51 int query(int x, int l, int r, int pos){ 52 if (l==r) return l; int mid=l+r>>1; 53 if (sum[ls[x]]>=pos) return query(ls[x],l,mid,pos); 54 else return query(rs[x],mid+1,r,pos-sum[ls[x]]); 55 } 56 57 int main(){ 58 read(n); read(m); read(T); 59 for (int i=1; i<=n; i++) read(h[i]), a[i]=h[i]; 60 sort(a+1,a+1+n); N=unique(a+1,a+1+n)-a-1; 61 for (int i=1,height; i<=n; i++) height=h[i],h[i]=lower_bound(a+1,a+1+N,h[i])-a,ori[h[i]]=height; 62 for (int i=1; i<=n; i++) insert(root[i],1,n,h[i]),fa[i]=i; 63 for (int i=1; i<=m; i++) read(e[i].u), read(e[i].v), read(e[i].w), e[i].q=0; 64 for (int i=1+m; i<=T+m; i++) read(e[i].u), read(e[i].w), read(e[i].v), e[i].q=1, e[i].id=i-m; 65 sort(e+1,e+1+m+T); 66 for (int i=1,cnt=0,u,v; i<=m+T; i++){ 67 if (!e[i].q){ 68 if (cnt==n-1) continue; 69 u=find(e[i].u), v=find(e[i].v); 70 if (u!=v){ 71 fa[u]=v; cnt++; 72 root[v]=merge(root[u],root[v]); 73 } 74 } 75 else{ 76 int x=find(e[i].u); 77 if (sum[root[x]]<e[i].v) ans[e[i].id]=-1; 78 else ans[e[i].id]=ori[query(root[x],1,n,sum[root[x]]-e[i].v+1)]; 79 } 80 } 81 for (int i=1; i<=T; i++) printf("%d\n", ans[i]); 82 return 0; 83 }
在线:
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int logn = 17, maxn = 200005; 6 struct node{ 7 int to,next; 8 }e[maxn]; 9 struct data{ 10 int u,v,w; 11 }E[maxn*3]; 12 int n,m,T,tot,cnt,ans,head[maxn],f[maxn],fa[maxn][logn+1],dep[maxn],mx[maxn][logn+1]; 13 int N,h[maxn],orz[maxn],a[maxn],top,low[maxn],high[maxn],root[maxn*2],st[maxn*2]; 14 int ls[5000005],rs[5000005],sum[5000005]; 15 16 void read(int &x){ 17 x=0; char c=getchar(); int f=1; 18 while (c<'0' || c>'9'){if (c=='-') f=-1; c=getchar();} 19 while (c>='0' && c<='9') x=x*10+c-48, c=getchar(); 20 x*=f; 21 } 22 bool operator<(data a, data b){return a.w<b.w;} 23 void insert(int u, int v){e[++tot].to=v; e[tot].next=head[u]; head[u]=tot;} 24 int find(int x){return f[x]==x?x:f[x]=find(f[x]);} 25 26 int getrt(int u, int val){ 27 for (int i=logn; i>=0; i--) 28 if (dep[u]>(1<<i) && mx[u][i]<=val) u=fa[u][i]; 29 return u; 30 } 31 32 void dfs(int u){ 33 st[++top]=u; dep[u]=dep[fa[u][0]]+1; 34 if (u>n) low[u]=top; 35 for (int i=1; i<=logn; i++) fa[u][i]=fa[fa[u][i-1]][i-1]; 36 for (int i=1; i<=logn; i++) mx[u][i]=max(mx[u][i-1],mx[fa[u][i-1]][i-1]); 37 for (int i=head[u],v; i; i=e[i].next) fa[v=e[i].to][0]=u,mx[v][0]=h[u],dfs(v); 38 if (u>n) st[++top]=u,high[u]=top; 39 } 40 41 void update(int &x, int l, int r, int last, int pos){ 42 x=++cnt; //注意这里千万不要if(!x) 43 sum[x]=sum[last]+1; 44 if (l==r) return; else ls[x]=ls[last],rs[x]=rs[last]; 45 int mid=l+r>>1; 46 if (pos<=mid) update(ls[x],l,mid,ls[last],pos); 47 else update(rs[x],mid+1,r,rs[last],pos); 48 } 49 50 int query(int l, int r, int x, int y, int rk){ 51 if (l==r) return l; 52 int mid=l+r>>1,s=sum[ls[y]]-sum[ls[x]]; //注意是 左子树的sum差 53 if (s>=rk) return query(l,mid,ls[x],ls[y],rk); 54 else return query(mid+1,r,rs[x],rs[y],rk-s); 55 } 56 57 void build(){ 58 for (int i=1; i<=m; i++) read(E[i].u),read(E[i].v),read(E[i].w); 59 sort(E+1,E+1+m); 60 N=n; tot=1; cnt=0; 61 for (int i=1; i<=2*n; i++) f[i]=i; 62 for (int i=1,tmp; i<=m; i++){ 63 int u=find(E[i].u), v=find(E[i].v); 64 if (u!=v){ 65 tmp=++N; 66 f[u]=f[v]=tmp; h[tmp]=E[i].w; 67 insert(tmp,v); insert(tmp,u); 68 if (N==2*n-1) break; 69 } 70 } 71 for (int i=1; i<=n; i++) if (!dep[i]) dfs(find(i)); 72 for (int i=1,x; i<=top; i++){ 73 x=st[i]; 74 if (x<=n) update(root[i],1,n,root[i-1],h[x]); 75 else root[i]=root[i-1]; 76 } 77 } 78 79 void solve(){ 80 while (T--){ 81 int v,x,k,t,a,b,tot; 82 read(v); read(x); read(k); 83 if (ans!=-1) v^=ans,x^=ans,k^=ans; 84 t=getrt(v,x); 85 a=low[t]; b=high[t]; 86 if ((tot=sum[root[b]]-sum[root[a]])<k) printf("%d\n", ans=-1); 87 else printf("%d\n", ans=orz[query(1,n,root[a],root[b],tot-k+1)]); 88 } 89 } 90 91 void pre(){ 92 read(n); read(m); read(T); 93 for (int i=1; i<=n; i++) read(h[i]),a[i]=h[i]; 94 sort(a+1,a+1+n); N=unique(a+1,a+1+n)-a-1; 95 for (int i=1,height; i<=n; i++) height=h[i],h[i]=lower_bound(a+1,a+1+N,h[i])-a,orz[h[i]]=height; 96 } 97 98 int main(){ 99 pre(); 100 build(); 101 solve(); 102 return 0; 103 }