BZOJ2286: [Sdoi2011]消耗战 LCA单调性+树形dp
思路:
首先对于一次询问进行一次朴素的树形dp极为容易.不过这样需要\(O(n)\)的时间复杂度,这样总的时间复杂度为\(O(qn)\),无法通过.
我们能否对于一次询问,将时间复杂度限定至仅与关键点数有关呢?
我们考虑构造一个树结构,仅仅将关键点连接起来,并适当添加部分非关键点,我们如果在这样减缩后的树上做dp,就能使得复杂度只与关键点数有关.
我们利用一个单调栈维护树上的一条深度单调递增的链,当我们要添加的点与栈顶的点的lca深度小于栈顶时,我们不断弹栈并在这个过程中顺便维护此时树的形态.
容易发现,对于每次添加,至多在新树上添加一个点,因此新树的点数只与关键点线性相关.
接下来在新树上作朴素的树形dp就行了.
#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
namespace Fio{
inline int getc(){
static const int L=1<<15;static char buf[L],*S=buf,*T=buf;
if(S==T){T=(S=buf)+fread(buf,1,L,stdin);if(S==T)return EOF;}
return*S++;
}
template<typename T>inline void Get(T&x){
int c;while(!isdigit(c=getc())&&c!='-');bool sign=c=='-';
x=sign?0:c-'0';while(isdigit(c=getc()))x=(x<<1)+(x<<3)+c-'0';
if(sign)x=-x;
}
char buf[5000010],*o=buf;
template<typename T>inline void print(T x){
static int stk[100];int top=0;if(x<0)*o++='-',x=-x;
if(!x)*o++=48;else{for(;x;x/=10)stk[++top]=x%10;for(int i=top;i>=1;--i)*o++=48+stk[i];}
*o++='\n';
}
inline void Final(){fwrite(buf,1,o-buf,stdout);}
}
#define N 250010
int head[N],next[N<<1],end[N<<1],len[N<<1];
inline void addedge(int a,int b,int x){
static int q=1;end[q]=b,next[q]=head[a],len[q]=x,head[a]=q++;
}
inline void make(int a,int b,int x){addedge(a,b,x),addedge(b,a,x);}
int pa[N][21],w[N][21],dfn[N],id,dep[N];
void dfs(int x,int fa){
dfn[x]=++id;
for(int j=head[x];j;j=next[j])if(end[j]!=fa){
dep[end[j]]=dep[x]+1,pa[end[j]][0]=x,w[end[j]][0]=len[j],dfs(end[j],x);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;--i)if(dep[pa[x][i]]>=dep[y])x=pa[x][i];
if(x==y)return x;
for(int i=20;i>=0;--i)if(pa[x][i]!=pa[y][i])x=pa[x][i],y=pa[y][i];
return pa[x][0];
}
long long calc(int son,int Anc){
int res=100010;for(int i=20;i>=0;--i)if(dep[pa[son][i]]>=dep[Anc])res=min(res,w[son][i]),son=pa[son][i];return res;
}
int seq[N],siz;
inline bool cmp(const int&a,const int&b){return dfn[a]<dfn[b];}
int stk[N],top,Anc[N];
struct Array{
long long sum[N];int t[N];
inline void modify(int x,int add,int v){
if(t[x]!=v)t[x]=v,sum[x]=0;sum[x]+=add;
}
inline long long ask(int x,int v){
if(t[x]!=v)t[x]=v,sum[x]=0;return sum[x];
}
}f,IsImp;int nowv;
int main(){
#ifndef ONLINE_JUDGE
freopen("tt.in","r",stdin);
#endif
using namespace Fio;
int n;Fio::Get(n);
register int i,j;int a,b,x;for(i=1;i<n;++i)Get(a),Get(b),Get(x),make(a,b,x);
dep[1]=1,dfs(1,-1);
for(j=1;j<=20;++j)
for(i=1;i<=n;++i)pa[i][j]=pa[pa[i][j-1]][j-1],w[i][j]=min(w[i][j-1],w[pa[i][j-1]][j-1]);
int Q;Get(Q);
while(Q--){
Get(siz);for(i=1;i<=siz;++i)Get(seq[i]);++nowv;for(i=1;i<=siz;++i)IsImp.modify(seq[i],1,nowv);seq[++siz]=1;
sort(seq+1,seq+siz+1,cmp);int end=siz;
for(top=0,i=1;i<=siz;++i){
if(!top)stk[++top]=seq[i];
else{
int Lca=lca(stk[top],seq[i]);Anc[seq[i]]=Lca;
while(dep[stk[top]]>dep[Lca]){if(dep[stk[top-1]]<=dep[Lca])Anc[stk[top]]=Lca;--top;}
if(stk[top]!=Lca)Anc[Lca]=stk[top],seq[++end]=stk[++top]=Lca;
stk[++top]=seq[i];
}
}
sort(seq+1,seq+end+1,cmp);
//for(i=1;i<=siz;++i)printf("%d ",seq[i]);puts("");
long long dis;
for(i=end;i>=1;--i){
dis=calc(seq[i],Anc[seq[i]]);
if(IsImp.ask(seq[i],nowv))f.modify(Anc[seq[i]],dis,nowv);else f.modify(Anc[seq[i]],min(dis,f.ask(seq[i],nowv)),nowv);
}
print(f.ask(1,nowv));
}
return Final(),0;
}
评论 (0)