[HNOI2016] 网络
前言
我不期望谁能看懂,写博客只是因为写这道题过程曲折,需要博客记录。
人话:需要写博客平静自己的心情。
题目
讲解
这道题思路不难。
为了方便讲解,我们将题目中的交互信息叫做路径,其重要度叫做该路径的权值。
首先我们考虑如果有很多路径已经挂在了树上,我们怎么回答询问?
考虑二分。
直接二分答案,判断合法性的时候直接将可能的路径求交集,然后判断询问的点是否在该路径交集上即可。
多个路径求交集可以将路径按权值排序后放到线段树上维护。
由于此题需要求大量的 \(\tt LCA\),所以我们需要高效的 \(\tt ST\) 来求 \(O(1)\) 求。
听着很简单是不是?等你写路径交集函数的时候就不会这么觉得了。
当然这道题也可以用整体二分做。
代码
//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 200005;
int n,m;
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int head[MAXN],tot;
struct edge
{
int v,nxt;
}e[MAXN << 1];
void Add_Edge(int x,int y)
{
e[++tot].v = y;
e[tot].nxt = head[x];
head[x] = tot;
}
void Add_Double_Edge(int x,int y)
{
Add_Edge(x,y);
Add_Edge(y,x);
}
int dfn[MAXN],dfntot,st[MAXN << 1][19],cnt,who[MAXN],lg[MAXN << 1],d[MAXN];
int cs(int x,int y){return d[x] > d[y] ? y : x;}
int cs2(int x,int y){return d[x] > d[y] ? x : y;}
void dfs(int x,int fa)
{
d[x] = d[fa] + 1;
dfn[x] = ++dfntot;
st[who[x] = ++cnt][0] = x;
for(int i = head[x]; i ;i = e[i].nxt)
{
if(e[i].v == fa) continue;
dfs(e[i].v,x);
st[++cnt][0] = x;
}
}
int opt[MAXN][4];
int lca(int x,int y)
{
int u = who[x],v = who[y];
if(u > v) swap(u,v);
int ll = lg[v-u+1];
return cs(st[u][ll],st[v-(1<<ll)+1][ll]);
}
int netot;
struct Network
{
int x,val;
Network(){}
Network(int x1,int val1){
x = x1;
val = val1;
}
bool operator < (const Network &px)const{
return val < px.val;
}
}net[MAXN];
int to[MAXN];
struct node
{
int u,v;
node(){}
node(int u1,int v1){
u = u1;
v = v1;
}
void fk(){if(dfn[u] > dfn[v]) swap(u,v);}
}t[MAXN << 2];
bool on(int x,node p)
{
if(p.u < 0) return 0;
int LCA = lca(p.u,p.v);
if(lca(x,LCA) != LCA) return 0;
p.fk();
int l1 = lca(p.u,x),l2 = lca(p.v,x);
if((l1 == LCA && l2 == x) || (l1 == x && l2 == LCA)) return 1;
return 0;
}
node merg(node p1,node p2)
{
if(!p1.u) return p2;
if(!p2.u) return p1;
if(p1.u < 0 || p2.u < 0) return node(-1,-1);
int LCA1 = lca(p1.u,p1.v),LCA2 = lca(p2.u,p2.v),cn1 = 0,cn2 = 0;
if(!on(LCA1,p2) && !on(LCA2,p1)) return node(-1,-1);
p1.fk(); p2.fk();
if(LCA1 == p1.u) cn1 = 1;
if(LCA2 == p2.u) cn2 = 1;
if(cn2) swap(p1,p2),swap(LCA1,LCA2),swap(cn1,cn2);
if(cn1 && !cn2)
{
if(cn2)//chain & chain
return node(dfn[p1.u] > dfn[p2.u] ? p1.u : p2.u,dfn[p1.v] < dfn[p2.v] ? p1.v : p2.v);
else//chain & turning
{
int V = cs2(cs2(lca(p1.u,p2.u),lca(p1.u,p2.v)),cs2(lca(p1.v,p2.u),lca(p1.v,p2.v)));
return node(on(LCA2,p1) ? LCA2 : p1.u,V);
}
}
else//turning & turning
{
if(LCA1 == LCA2)
{
node ret1 = node(lca(p1.u,p2.u),lca(p1.v,p2.v));
node ret2 = node(lca(p1.u,p2.v),lca(p1.v,p2.u));
if(ret1.u == ret1.v) return ret2;
return ret1;
}
int U,V;
if(on(LCA1,p2)) U = LCA1;
else U = LCA2;
V = cs2(cs2(lca(p1.u,p2.u),lca(p1.u,p2.v)),cs2(lca(p1.v,p2.u),lca(p1.v,p2.v)));
return node(U,V);
}
}
#define lc (x<<1)
#define rc (x<<1|1)
struct SegmentTree
{
int ex[MAXN << 2];
void up(int x){t[x] = merg(t[lc],t[rc]);ex[x] = ex[lc] + ex[rc];}
void Add(int x,int l,int r,int pos,node ad)
{
if(l == r) {t[x] = ad;ex[x] = 1;return;}
int mid = (l+r) >> 1;
if(pos <= mid) Add(lc,l,mid,pos,ad);
else Add(rc,mid+1,r,pos,ad);
up(x);
}
void Del(int x,int l,int r,int pos)
{
if(l == r) {t[x] = node(0,0);ex[x] = 0;return;}
int mid = (l+r) >> 1;
if(pos <= mid) Del(lc,l,mid,pos);
else Del(rc,mid+1,r,pos);
up(x);
}
int Query(int x,int l,int r,int pos,node now)
{
if(l == r) return net[l].val;
node n1 = merg(now,t[rc]);
int mid = (l+r) >> 1;
if(!on(pos,n1) && ex[rc]) return Query(rc,mid+1,r,pos,now);
return Query(lc,l,mid,pos,n1);
}
}ST;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read();
for(int i = 1;i < n;++ i) Add_Double_Edge(Read(),Read());
dfs(1,0);
lg[0] = -1;
for(int i = 1;i <= cnt;++ i) lg[i] = lg[i>>1] + 1;
for(int i = 1;i <= lg[cnt];++ i)
for(int j = 1;j+(1<<i)-1 <= cnt;++ j)
st[j][i] = cs(st[j][i-1],st[j+(1<<(i-1))][i-1]);
for(int i = 1;i <= m;++ i)
{
opt[i][0] = Read();
if(!opt[i][0])
{
for(int j = 1;j <= 3;++ j) opt[i][j] = Read();
net[++netot] = Network(i,opt[i][3]);
}
else opt[i][1] = Read();
}
sort(net+1,net+netot+1);
for(int i = 1;i <= netot;++ i) to[net[i].x] = i;
for(int i = 1;i <= m;++ i)
{
if(!opt[i][0]) ST.Add(1,1,netot,to[i],node(opt[i][1],opt[i][2]));
else if(opt[i][0] == 1) ST.Del(1,1,netot,to[opt[i][1]]);
else
{
if(!ST.ex[1] || on(opt[i][1],t[1])) Put(-1,'\n');
else Put(ST.Query(1,1,netot,opt[i][1],node(0,0)),'\n');
}
}
return 0;
}
在画了一万张路径相交的图之后,终于写出了这份代码。
此代码已经经过精简(指删掉调试信息),但仍然有215行。
似乎删掉调试信息之前有260行+。