luogu P3345 [ZJOI2015]幻想乡战略游戏
算是一道动态点分治入门题(我太弱了加起来学了三天)。
拿到题首先考虑如何暴力,我们可以随机钦定一个点(不妨就设\(1\))为根节点,然后算出补给站设在该点的总花费,然后算出补给站在每个儿子时的总花费,显然最多只会有一个儿子花费比它(当前点)本身小(当且仅当\(sum[u]<sum[son] \times 2\),\(sum[i]\)表示\(i\)号点子树内拥有的军队总数),如果存在这个花费比它小的儿子,就跳到它儿子上重复上述过程,如果不存在这个儿子,那么这个点就是答案。
显然这样写一条链就没了,所以考虑用点分树来优化这个过程(下面其实就是动态点分治的套路了,跟本题关系不太大)。
记三个值\(sum[i],dis1[i],dis2[i]\)分别表示当前子树内的军队总数、当前子树内的点到当前子树根节点的总花费、当前子树内的点到当前子树根节点的父亲节点的总花费(注意这里所有子树的概念都是在点分树中)。
这三个值在修改是可以暴力跳点分树维护,查询的时候也是暴力跳点分树就好了。时间复杂度\(O(nlog^2n)\)
\(tips:\)今天学会了如何\(O(nlogn)\)预处理\(ST\)表然后\(O(1)\)求\(LCA\),其实就是类似一个欧拉序的东西,按照\(dep\)建立\(ST\)表,然后就能\(O(1)\)得到\(LCA\)了。
代码:
#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define rint int
using namespace std;
typedef long long LL;
const int N=1000009;
int n,Q,root,head[N],cnt,F[N],log[N];
LL dis1[N],dis2[N],sum[N];
struct Edge
{
int nxt,to,w;
}g[N*2];
struct G
{
int head[N],cnt,f[N][30],dep[N],del[N],siz[N],Euler[N],Fst[N],Index;
LL dis[N];
struct Edge
{
int nxt,to,w;
}g[N*2];
void add(int from,int to,int w)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
head[from]=cnt;
}
int LCA(int x,int y)
{
if(Fst[x]>Fst[y])
swap(x,y);
x=Fst[x],y=Fst[y];
int k=log[y-x+1];
return dep[f[x][k]]<dep[f[y-(1<<k)+1][k]]?f[x][k]:f[y-(1<<k)+1][k];
}
void dfs(int x,int fa)
{
Euler[++Index]=x,Fst[x]=Index,f[Index][0]=x;
for (rint i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
dep[v]=dep[x]+1,dis[v]=dis[x]+g[i].w;
dfs(v,x);
Euler[++Index]=x,f[Index][0]=x;
}
}
int Get_dis(int x,int y)
{
return dis[x]+dis[y]-2*dis[LCA(x,y)];
}
void DFS(int x,int fa)
{
siz[x]=1;
for (rint i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
DFS(v,x);
siz[x]+=siz[v];
}
}
int Get_Weight(int x)
{
DFS(x,-1);
int k=siz[x]/2,fa=-1;
while(1)
{
int tmp=0;
for (rint i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
if(siz[v]>siz[tmp])
tmp=v;
}
if(siz[tmp]<=k)
return x;
fa=x,x=tmp;
}
}
void work()
{
dfs(1,-1);
for (int j=1;1<<j<=Index;j++)
for (int i=1;i+(1<<j)-1<=Index;i++)
if(dep[f[i][j-1]]<dep[f[i+(1<<j-1)][j-1]])
f[i][j]=f[i][j-1];
else
f[i][j]=f[i+(1<<j-1)][j-1];
}
}T;
void add(int from,int to,int w)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
head[from]=cnt;
}
int read()
{
char c=getchar();
int x=0,f=1;
while(c<'0'||c>'9')
{
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
x=x*10+c-'0',c=getchar();
return x*f;
}
void init()
{
log[0]=-1;
for (int i=1;i<=N-9;i++)
log[i]=log[i>>1]+1;
n=read(),Q=read();
for (rint i=1;i<n;i++)
{
int x,y,z;
x=read(),y=read(),z=read();
T.add(x,y,z),T.add(y,x,z);
}
T.work();
}
void build(int fa)
{
T.del[fa]=1;
for (rint i=T.head[fa];i;i=T.g[i].nxt)
{
int v=T.g[i].to;
if(T.del[v]||v==fa)
continue;
int w=T.Get_Weight(v);
F[w]=fa,add(fa,w,v),add(w,fa,v);
build(w);
}
}
void Modify(int x,int y)
{
//printf("eijfie %d %d\n",x,y);
for (rint i=x;i;i=F[i])
{
sum[i]+=y;
if(i!=root)
{
LL fuck=1LL*T.Get_dis(x,F[i])*y;
//if(x==3&&y==1)
//printf("???%d %d %d\n",x,F[i],T.Get_dis(2,3));
dis1[F[i]]+=fuck,dis2[i]+=fuck;
}
}
}
LL calc(int x)
{
LL ans=dis1[x];
for (rint i=x;F[i];i=F[i])
{
ans+=dis1[F[i]]-dis2[i];
ans+=(sum[F[i]]-sum[i])*T.Get_dis(x,F[i]);
}
return ans;
}
LL Query(int x)
{
LL last=calc(x);
//printf("fuck%d %d\n",x,last);
for (rint i=head[x];i;i=g[i].nxt)
{
if(g[i].to==F[x])
continue;
LL now=calc(g[i].w);
//printf("%lld %d\n",now,g[i].w);
if(now<last)
return Query(g[i].to);
}
return last;
}
void print(int x,int fa)
{
printf("%d %d\n",fa,x);
for (rint i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
print(v,x);
}
}
void work()
{
root=T.Get_Weight(1);
build(root);
//printf("weoigwi%d\n",T.LCA(2,3));
//print(root,-1);
for (rint _=1;_<=Q;_++)
{
int x,y;
x=read(),y=read();
Modify(x,y);
//if(_==4)
//printf("wyj%d\n",dis1[2]);
printf("%lld\n",Query(root));
}
}
int main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!