[CodeChef] Maximum and Minimum
前言
写了我半个上午,白天剩下的时间都在调。
成功刷新代码长度上限:7k,270行。
题目
CodeChef
Vjudge
讲解
这路径权值显然就是最小生成树路径上的最大值,所以先跑 \(\tt Kruskal\)。
我们思考如果只有一棵树怎么做,其实就是这道题,思路是差分,把点(LCA)处的权值转化为点到根的权值。
首先我们对 \(\tt G1\) 建一棵 \(\tt Kruskal\) 重构树,用全局平衡二叉树(重剖会多个 \(\log\))来支持修改与查询。
然后对 \(\tt G2\) 的最小生成树进行点分治,每次点分治把所有点取出按点到分治中心的路径权值从小到大排序。
依次在 \(\tt G1\) 中插入,然后询问权值即可。
时间复杂度 \(O(n\log_2n)\),常数略大。
代码
$\tt CodeChef$ 机子很快,爱了。
//12252024832524
#include
#define TT template
using namespace std;
typedef long long LL;
const int MAXN = 200005;
const int MOD = 998244353;
int n,m,Ans;
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[2][MAXN],tot;
struct edge
{
int v,w,nxt;
}e[MAXN<<2];
void Add_Edge(int opt,int u,int v,int w = 0)
{
e[++tot] = edge{v,w,head[opt][u]};
head[opt][u] = tot;
}
void Add_Double_Edge(int opt,int u,int v,int w = 0)
{
Add_Edge(opt,u,v,w);
Add_Edge(opt,v,u,w);
}
struct EDGE
{
int u,v,w;
void Get(){u = Read(),v = Read(),w = Read();}
bool operator < (const EDGE &px)const{
return w < px.w;
}
}E[MAXN<<1];
int F[MAXN];//pot2 这玩意不能共享
int findSet(int x)
{
if(F[x] ^ x) F[x] = findSet(F[x]);
return F[x];
}
int siz[MAXN],val[MAXN],son[MAXN],ppl[MAXN],d[MAXN],f[MAXN],cf[MAXN];//val : Kruskal重构树上的点权
void dfs1(int x,int fa)
{
d[x] = d[fa] + 1; siz[x] = 1; f[x] = fa;
for(int i = head[0][x],v; i ;i = e[i].nxt)
if((v = e[i].v) ^ fa)
{
dfs1(v,x);
siz[x] += siz[v];
if(siz[v] > siz[son[x]]) son[x] = v;
}
ppl[x] = siz[x] - siz[son[x]];
}
int tp[MAXN],dfn[MAXN],rdfn[MAXN],dfntot;
void dfs2(int x,int t)
{
tp[x] = t; rdfn[dfn[x] = ++dfntot] = x;//pot1
if(!son[x]) return;
dfs2(son[x],t);
for(int i = head[0][x],v; i ;i = e[i].nxt)
if((v = e[i].v) != f[x] && v != son[x])
dfs2(v,v);
}
#define lc t[x].ch[0]
#define rc t[x].ch[1]
struct node
{
int ch[2],s,cnt,lz,ans,sz,fa;
}t[MAXN];
void calc(int x,int v)
{
if(!x) return;
t[x].lz += v; if(t[x].lz >= MOD) t[x].lz -= MOD; //pot8
t[x].cnt += v; if(t[x].cnt >= MOD) t[x].cnt -= MOD; //pot6
t[x].ans = (t[x].ans + 1ll * v * t[x].s) % MOD;
}
void up(int x){t[x].ans = (t[lc].ans + t[rc].ans + 1ll * t[x].cnt * cf[x]) % MOD;}
void down(int x)
{
if(!t[x].lz) return;
calc(lc,t[x].lz);
calc(rc,t[x].lz);
t[x].lz = 0;
}
int tmp[MAXN],rt[MAXN];
void Build(int &x,int l,int r,int fa,int S)
{
for(int i = l,now = 0;i <= r;++ i)
{
now += ppl[tmp[i]];
if((now << 1) >= S)
{
x = tmp[i];//pot2
t[x].s = (cf[x] + MOD) % MOD;//pot6
t[x].sz = r-l+1;
if(l <= i-1) Build(lc,l,i-1,x,now-ppl[tmp[i]]);
if(i+1 <= r) Build(rc,i+1,r,x,S-now);
break;
}
}
t[x].s = (0ll + t[x].s + t[lc].s + t[rc].s) % MOD;
if(t[x].s < 0) t[x].s += MOD; up(x);
}
void Build(int x)
{
int S = 0,cur = 0;
for(int u = x; u ;u = son[u]) tmp[++cur] = u,S += ppl[u];
Build(rt[x],1,cur,f[x],S);
}
void Add(int x,int r,int v)//注意这棵全局平衡二叉树是dfn[x]有序
{
if(dfn[x]+t[rc].sz <= dfn[r]) {calc(x,v);return;}//pot9
if(dfn[x] <= dfn[r]) t[x].cnt = (t[x].cnt + v) % MOD,t[x].ans = (t[x].ans + 1ll * v * cf[x]) % MOD;//pot5
down(x);
Add(lc,r,v); if(dfn[r] > dfn[x]) Add(rc,r,v);
up(x);
}
int Query(int x,int r)
{
if(dfn[x]+t[rc].sz <= dfn[r]) return t[x].ans;
LL ret = 0; down(x);
if(dfn[x] <= dfn[r]) ret = 1ll * t[x].cnt * cf[x];//pot3
ret += Query(lc,r); if(dfn[r] > dfn[x]) ret += Query(rc,r);
return ret % MOD;
}
void Add_To_Root(int x,int v)
{
while(x)
{
Add(rt[tp[x]],x,v);
x = f[tp[x]];
}
}
int Query_To_Root(int x)
{
int ret = 0;
while(x)
{
ret = (ret + Query(rt[tp[x]],x)) % MOD;
x = f[tp[x]];
}
return ret;
}
int MAX[MAXN],RT;
bool vis[MAXN];
void getrt(int x,int fa,int S)
{
siz[x] = 1; MAX[x] = 0;//这波共享siz数组了
for(int i = head[1][x],v; i ;i = e[i].nxt)
if(!vis[v = e[i].v] && v != fa)
{
getrt(v,x,S);
siz[x] += siz[v];
MAX[x] = Max(MAX[x],siz[v]);
}
MAX[x] = Max(MAX[x],S-siz[x]);
if(!RT || MAX[x] < MAX[RT]) RT = x;
}
void getsiz(int x,int fa)
{
siz[x] = 1;
for(int i = head[1][x],v; i ;i = e[i].nxt)
if(!vis[v = e[i].v] && v != fa)
{
getsiz(v,x);
siz[x] += siz[v];
}
}
int mxw[MAXN],pt;
void getpoint(int x,int fa)
{
tmp[++pt] = x;
for(int i = head[1][x],v; i ;i = e[i].nxt)
if(!vis[v = e[i].v] && v != fa)
mxw[v] = Max(mxw[x],e[i].w),getpoint(v,x);
}
LL tt;
void solve(int x,int fa,int sg)
{
pt = 0;
for(int i = head[1][x],v; i ;i = e[i].nxt)
if(!vis[v = e[i].v] && v != fa)
{
mxw[v] = e[i].w;
if(fa) mxw[v] = Max(mxw[v],mxw[x]);//pot7
getpoint(v,x);
}
sort(tmp+1,tmp+pt+1,[](int x,int y){if(mxw[x] ^ mxw[y]) return mxw[x] < mxw[y];return x < y;});
Add_To_Root(x,sg);
tt += pt;
for(int i = 1;i <= pt;++ i)
{
Ans = (Ans + 1ll * mxw[tmp[i]] * Query_To_Root(tmp[i])) % MOD;
Add_To_Root(tmp[i],sg);
}
Add_To_Root(x,MOD-sg);
for(int i = 1;i <= pt;++ i) Add_To_Root(tmp[i],MOD-sg);
}
void dfs3(int x)
{
vis[x] = 1; getsiz(x,0);
solve(x,0,1);
for(int i = head[1][x],v; i ;i = e[i].nxt)
if(!vis[v = e[i].v]) solve(v,x,MOD-1);
for(int i = head[1][x],v; i ;i = e[i].nxt)
if(!vis[v = e[i].v])
RT = 0,getrt(v,x,siz[v]),dfs3(RT);
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read();
//G1
for(int i = 1;i <= m;++ i) E[i].Get();
sort(E+1,E+m+1);
for(int i = 1;i <= n;++ i) F[i] = i;
for(int i = 1,U,V,now = n;i <= m;++ i)
if((U = findSet(E[i].u)) ^ (V = findSet(E[i].v)))
{
++now; F[now] = now;
Add_Double_Edge(0,now,U);//pot4
Add_Double_Edge(0,now,V);
F[U] = now; F[V] = now; val[now] = E[i].w;
}
dfs1((n<<1)-1,0); dfs2((n<<1)-1,(n<<1)-1);
for(int i = 1;i < (n<<1);++ i)
{
cf[i] = val[i] - val[f[i]];
if(cf[i] < 0) cf[i] += MOD;
}
for(int i = 1;i < (n<<1);++ i) if(rdfn[i] == tp[rdfn[i]]) Build(rdfn[i]);
//G2
for(int i = 1;i <= m;++ i) E[i].Get();
sort(E+1,E+m+1);
for(int i = 1;i <= n;++ i) F[i] = i;
for(int i = 1,U,V;i <= m;++ i)
if((U = findSet(E[i].u)) ^ (V = findSet(E[i].v)))
Add_Double_Edge(1,E[i].u,E[i].v,E[i].w),F[U] = V;
getrt(1,0,n); dfs3(RT);
Put(Ans,'\n');
return 0;
}
/*
首先我们对G1建一棵Kruskal重构树,用全局平衡二叉树(重剖多个log)来支持修改与查询。
然后对G2的最小生成树进行点分治,每次点分治把所有点取出按mxw[u]从小到大排序
小的用差分的形式在G2中插进去,然后用大的在G2中查询
*/