。。。。

1 篇文章 0 订阅

有一只很强的怪//给它随便起个名字好了,就叫HLJ吧

地图可以抽象成一棵树

你可以在任意节点上召唤它

你有一些士兵,每个士兵也在一个节点上

士兵有射程,只能攻击树上路径距离不超过W的点//ps:住手,这根本不是平面图好吧

HLJ出现在节点上的时候,只会被攻击到一次,当HLJ与士兵在同一个节点上的时候,默认士兵的攻击先发动

HLJ有仇恨,从最后一次伤害来源中任取一个

当然了,士兵也可以选择不进行攻击

特别的,你并不清楚HLJ的具体血量

因此,要求对于任意血量N,你都能保证击败HLJ

求最小射程W,题目特性(存在两个士兵不在同一个节点上)保证有解

多组询问

首先,显然的,肯定是在一条边上耗死

即求

我们考虑虚树求解

先二分转判定看看,以下均为描述check(k)

考虑一条边在某个关键点对中的一个点x到LCA的路径上 描述为一条son到f的边

对于son点(以下称为标记点)若满足dist(x,f)<=k且存在一个son子树外的点到son的dist<=k

那么答案合法

建出虚树,倍增找出每个关键点最远可标记的点,考虑对这一条链上的点打标记,记为mark[x]

在建虚树的时候,还要记录每个点到其父亲最底端的节点

标记更新的时候分两种情况,标记是否在链顶的上端,讨论一下更新

据说分是否超过一百选择暴力统计点对或树形DP能过?

匀一匀大概是O(100^2*(M/100)*log(n)+(M/100)*n) M=sigma K(询问点数)=2e5 N=1e5

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
using namespace std;
const int N=2e5+10,M=N<<1,INF=0x3f7f7f7f,LOG=20;
namespace IO{
    const int ios=1<<17;
    char R[ios],P[ios],*Rc=R,*RB=R,*Pc=P;
    const char*PB=P+ios;
    int stk[50],tp=0,pass;
    inline void flush(){fwrite(P,1,Pc-P,stdout);Pc=P;}
    inline int gec(){return(Rc==RB&&(RB=(Rc=R)+fread(R,1,ios,stdin),Rc==RB))?EOF:*Rc++;}
    inline void puc(const char&c){if(Pc==PB)flush();*(Pc++)=c;}
    template<typename Tp>inline int read(Tp&A){
        static int c,sg;c=gec();sg=0;A=0;
        while(!isdigit(c)&&c!=EOF)sg|=(c=='-'),c=gec();
            if(c==EOF)return EOF;
        while(isdigit(c))A=(A<<3)+(A<<1)+(c^'0'),c=gec();
        return 0;
    }
    inline int read(){return read(pass),pass;}
    template<typename Tp>inline void print(Tp A){
        if(A<0)puc('-');
        if(!A)puc('0');
        for(int Q,P;A;A=Q)Q=A/10,P=A-10*Q,stk[++tp]=P^'0';
        while(stk[tp])puc(stk[tp--]);
    }
}
using IO::gec;using IO::puc;using IO::read;using IO::print;using IO::flush;
int s[N][LOG];
int fr[N],nx[M],to[M],w[M],E;
int fir[N],nex[M],toe[M],we[M],ED,q[N],que[N];
int dfn[N],DFN,dep[N]={INF},deep[N],mark[N],mind[N];
int n,m,res,K,key,Min_L,flag,ques;
bool vis[N],has[N];
inline void adde(int x,int y,int W){
	to[++E]=y;w[E]=W;nx[E]=fr[x];fr[x]=E;
}
inline int skp(int x,int ss){
	for(int i=LOG-1;~i;i--)
		if(s[x][i]>1&&dep[s[x][i]]>ss)
			x=s[x][i];
	return x;
}
inline void link(int x,int y){
	toe[++ED]=y;we[ED]=dep[y]-dep[x];nex[ED]=fir[x];fir[x]=ED;
	mind[y]=dep[skp(y,dep[x])];
}
inline int depth_cmp(int a,int b){return dep[a]<dep[b]?a:b;}
inline bool dfn_cmp(int a,int b){return dfn[a]<dfn[b];} 
void dfs(int x,int f){
	dfn[x]=++DFN;s[x][0]=f;
	for(int i=fr[x];i;i=nx[i])
		if(to[i]!=f){
			deep[to[i]]=deep[x]+1;
			dep[to[i]]=dep[x]+w[i];
			dfs(to[i],x);
		}
}
inline int LCA(int x,int y){
	if(deep[x]<deep[y])swap(x,y);
	int S=deep[x]-deep[y];
	for(int i=0;i<LOG;i++,S>>=1)
		if(S&1)x=s[x][i];
	if(x==y)return x;
	for(int i=LOG-1;~i;i--)
		if(s[x][i]!=s[y][i])
			x=s[x][i],y=s[y][i];
	return s[x][0];
}
inline int skip(int x,int ss){
	int lim=dep[x]-ss;
	if(dep[s[x][0]]<lim)return -1;
	for(int i=LOG-1;~i;i--)
		if(s[x][i]>1&&dep[s[s[x][i]][0]]>=lim)
			x=s[x][i];
	return x;
}
int DPrun(int x){
	int A=has[x]?0:INF,B=INF;
	for(int i=fir[x];i;i=nex[i]){
		int tmp=DPrun(toe[i])+we[i];
		if(tmp<A)B=A,A=tmp;
		else if(tmp<B)B=tmp;
	}
	Min_L=min(Min_L,A+B);
	return A;
}
struct data{
	data(int a,int c,int d):a(a),c(c),d(d){}
	int a,c,d;
};
data run(int x){
	int A=has[x]?0:INF,C=INF,D=mark[x]?mark[x]:INF;
	for(int i=fir[x];i;i=nex[i]){
		data s=run(toe[i]);
		int tmp=s.a+we[i],temp=s.c+we[i],ter=s.d;
		if(C+tmp<=key)flag=1;
		if(A+temp<=key)flag=1;
		if(tmp<A)A=tmp;
		if(temp<C)C=temp;
		if(ter<D)D=ter;
	}
	if(D<=mind[x])C=mind[x]-dep[x];
	else C=min(C,D-dep[x]);
	return data(A,C,D);
}
int stk[N<<1],top;
inline void build(int m){
	sort(q+1,q+m+1,dfn_cmp);ques=0;
	int top=1;stk[1]=1;que[++ques]=1;
	for(int i=1;i<=m;i++){
		if(q[i]==q[i-1])continue;
		int S=LCA(stk[top],q[i]);
		if(S!=stk[top]){
			while(dfn[stk[top-1]]>dfn[S]&&top>1)
				link(stk[top-1],stk[top]),top--;
			if(stk[top-1]==S&&top>1)
				link(stk[top-1],stk[top]),top--;	
			else 
				if(top>1)link(S,stk[top]),stk[top]=S,que[++ques]=S;
		}
		stk[++top]=q[i],que[++ques]=q[i];
	}
	while(top>1)link(stk[top-1],stk[top]),top--;
}
bool check(int step){
	int tot=K;
	for(int i=1;i<=K;i++){
		int v=skip(q[i],step);
		if(v!=-1)mark[q[i]]=dep[v];
	}
	key=step;flag=0;
	run(1);
	for(int i=1;i<=K;i++)mark[q[i]]=0;
	return flag;
}
int solve(){
	build(K);
	Min_L=INF;DPrun(1);
	int l=Min_L>>1,r=Min_L;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}return l;
}
int main(){
	freopen("laqiang.in","r",stdin);
	freopen("laqiang.out","w",stdout);
	read(n);read(m);q[0]=1;
	for(int i=1,u,v,W;i<n;i++){
		read(u);read(v);read(W);
		adde(u,v,W);adde(v,u,W);
	}
	dfs(1,0);
	for(int i=1;i<LOG;i++)
		for(int j=1;j<=n;j++)
			s[j][i]=s[s[j][i-1]][i-1];
	for(int i=1;i<=m;i++){
		int cur=0;
		read(K);res=INF;
		for(int j=1;j<=K;j++)read(q[j]),has[q[j]]=1;
		printf("%d\n",solve());
		for(int j=1;j<=K;j++)has[q[j]]=0;
		for(int i=1;i<=ques;i++)fir[que[i]]=0;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值