长链贪心+虚树+类直径合并性+分块建树维护ST表:1008T4

http://47.92.197.167:5283/contest/408/problem/4

两个可以推的经典套路:

  1. 我们可以对所有点建序树,然后取前 k k k 大。而取前 k k k 大可以通过以直径端点为根长剖来贪心
  2. 直径具有合并性(不仅是连通块,而且是点集)。同理,前 k k k 大的点也具有合并性。

想到这里,我们就已经知道对于相应点集如何求答案。同时知道如何对两个点集进行合并,我们就可以直接上线段树了。

然而可以更优化。我们可以考虑分块。以 k k k 分块。查询连续块我们可以用ST表预处理。散块我们和连续块的一起跑一遍虚树。

// 6k
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//mt19937 rand(time(0));
//mt19937_64 rand(time(0));
//srand(time(0));
#define N 200010
//#define M
//#define mo
int n, m, i, j, k, T, lstans;
inline void decode(int &l, int &r, int &k, int lstans, int testop) {
	lstans %= 19260817;
	if(testop) {
		l ^= lstans; l = (l % n + n) % n + 1;
		r ^= lstans; r = (r % n + n) % n + 1;
		if(l > r) std :: swap(l, r);
		k ^= lstans;
		k = (k % std :: min(r - l + 1, 100ll)) + 1;
	}
}
struct node {
	int y, z; 
};
vector<node>G[N], Xu[N]; 
int dep[N], dfn[N], f[N][22], tot; 
int h[N], Log2[N], id, op, K, p[N]; 
int l, r, L[N], R[N], s[N], q, li, ri; 

int lca(int x, int y) {
  if(x==y) return x; 
//  	printf("1(%lld %lld) %lld %lld\n", x, y, dep[x], dep[y]); 

  if(dep[x]<dep[y]) swap(x, y); 
//    	printf("1(%lld %lld) %lld %lld\n", x, y, dep[x], dep[y]); 

  for(int k=20; k>=0; --k)
    if(dep[f[x][k]]>=dep[y]) x=f[x][k]; 
//      	printf("2(%lld %lld) %lld %lld\n", x, y, dep[x], dep[y]); 

  if(x==y) return x; 
  for(int k=20; k>=0; --k)
    if(f[x][k]!=f[y][k]) x=f[x][k], y=f[y][k]; 
  return f[x][0]; 
}

void dfs(int x, int fa) {
	dfn[x]=++tot; dep[x]=dep[fa]+1; 
	for(auto t : G[x]) {
		int y=t.y, z=t.z; 
		if(y==fa) continue; 
//		printf("%d -> %d\n", x, y); 
		h[y]=h[x]+z; f[y][0]=x; 
		dfs(y, x); 
	}
}
		
		
vector<int>F; 
int mxh[N], son[N], w[N], H[N]; 
int summmm; 
map<int, int>mp; 

struct Xu_tree {
	vector<int>s, ans; 
	int x, y, z, i, n, rt1, rt2; 
	void dfs_zhi(int x, int fa, int &rt) {
		if(H[x]>H[rt]) rt=x; 
		for(auto t : Xu[x]) {
			int y=t.y, z=t.z; 
			if(y==fa) continue;
			H[y]=H[x]+z; 
			dfs_zhi(y, x, rt); 
		}
	}
	void dfs1(int x, int fa) {
		mxh[x]=son[x]=0; 
		for(auto t : Xu[x]) {
			int y=t.y, z=t.z; 
			if(y==fa) continue; 
			dfs1(y, x); mxh[y]+=z; 
//			printf("%lld : %lld\n", )
			if(mxh[y]>mxh[son[x]]) son[x]=y; 
		}
		mxh[x]=mxh[son[x]]; 

//		printf("mxh[%lld]=%lld\n", x, mxh[x]); 
//		mxh[x]=mxh[son[x]]; 
	}
	void dfs2(int x, int fa, int S) {
		w[x]=S; 
//		printf("w[%lld]=%lld\n", x, w[x]); 
		for(auto t : Xu[x]) {
			int y=t.y, z=t.z; 
			if(y==fa) continue; 
//			printf("%lld -> %lld (%lld) %lld\n", x, y, son[x], z); 
			w[x]=0; 
			if(y==son[x]) dfs2(y, x, S+z); 
			else dfs2(y, x, z); 
		}
	}
	void build() {
		F.clear(); mp.clear(); 
//		printf("%lld\n", s.size()); 
//		summmm+=s.size(); 
		sort(s.begin(), s.end(), [] (int x, int y) { return dfn[x]<dfn[y]; }); 
		n=unique(s.begin(), s.end())-s.begin(); 
		s.resize(n); 
//		for(auto i : s) printf("%lld ", i); printf("\n"); 
		n=s.size(); F.pb(s[0]); 
		for(int i=1; i<n; ++i) {
			x=s[i-1]; y=s[i]; z=lca(x, y); mp[x]=mp[y]=1; 
//			printf("lca(%d, %d)=%d\n", x, y, z); 
			F.pb(y); F.pb(z); 
		}
//		printf("%lld\n", F.size()); 
		sort(F.begin(), F.end(), [] (int x, int y) { return dfn[x]<dfn[y]; }); 
		n=unique(F.begin(), F.end())-F.begin();
		F.resize(n); 
//		summmm+=F.size(); 
		
		for(auto i : F) Xu[i].clear(); 		
		n=F.size(); 
		for(int i=1; i<n; ++i) {
			x=F[i-1]; y=F[i]; z=lca(x, y); 
//			printf("%d -> %d\n", y, z); 
			Xu[z].pb({y, h[y]-h[z]}); 
			Xu[y].pb({z, h[y]-h[z]}); 
		}
		H[F[0]]=rt1=0; dfs_zhi(F[0], 0, rt1); 
//		H[rt1]=0; dfs(rt1, 0, rt2); 
		
		dfs1(rt1, 0); 
		dfs2(rt1, 0, 0);
		sort(F.begin(), F.end(), [] (int x, int y) { return w[x]>w[y]; }) ; 
//		for(auto i : F) printf("%lld ", i); printf("\n"); 
//		for(auto i : F) printf("%lld ", w[i]); printf("\n"); 

		s.clear(); ans.clear(); 
		for(int i=0; i<n; ++i) {
			if(!mp[F[i]]) continue; 
			ans.pb(w[F[i]]); s.pb(F[i]); 
			if(ans.size()>K) break; 
		}
		s.pb(rt1); 
//		printf("rt1 : %lld\n", rt1); 
//		for(auto i : ans) printf("%lld ", i); printf("\n"); 
//		for(auto i : s) printf("%lld ", i); printf("\n"); 
	}
	void merge(Xu_tree A) {
//		s.clear(); 
		for(auto i : A.s) s.pb(i); 
	}
	int que(int x) {
		int Ans=0; n=ans.size(); 
		for(i=0; i<min(x, n); ++i)
			Ans+=ans[i]; 
		return Ans; 
	}
}dp[N][12], As;

void Merge(int l, int r) {
	int k = Log2[r-l+1]; 
//	printf("[%lld %lld] %lld\n", l, r, k);    
	As.merge(dp[l][k]); 
	As.merge(dp[r-(1<<k)+1][k]); 
}

signed main()
{
//	freopen("in.txt", "r", stdin);
//	freopen("out.txt", "w", stdout);
		freopen("nomenclature.in", "r", stdin);
	freopen("nomenclature.out", "w", stdout);
//	T=read();
//	while(T--) {
//
//	}
	id=read(); op=read(); n=read(); K=300; 
	for(i=1; i<n; ++i) {
		int u, v, w; 
		u=read(); v=read(); w=read(); 
		G[u].pb({v, w}); G[v].pb({u, w}); 
	}
	dfs(1, 0); 
	for(k=1; k<=20; ++k)
		for(i=1; i<=n; ++i) 
			f[i][k]=f[f[i][k-1]][k-1]; 
	for(i=1; i<=n; ++i) p[i]=read(); 
	for(l=i=1; l<=n; l=r+1, ++i) {
		r=min(n, l+K-1); L[i]=l; R[i]=r; 
		for(j=l; j<=r; ++j) s[j]=i, dp[i][0].s.pb(p[j]); 
		dp[i][0].build(); 
	}
//	int cntt=0; 
	m=n/K+1; 
//	printf("%lld\n", m); 
	for(i=2; i<=n; ++i) Log2[i]=Log2[i>>1]+1; 
	for(k=1; k<=22; ++k)
		for(i=1; i+(1<<k)-1<=m; ++i){
//			++cntt; 
			dp[i][k].merge(dp[i][k-1]); 
			dp[i][k].merge(dp[i+(1<<k-1)][k-1]); 
			dp[i][k].build(); 
		}
//	printf("%lld %lld\n", cntt, summmm); 
	printf("-------\n"); 

//
	q=read(); 
	while(q--) {
		l=read(); r=read(); k=read(); 
		decode(l, r, k, lstans, op); 
//		printf("[%lld %lld] %lld\n", l, r, k); 
		if(k==1) { printf("0\n"); lstans=0; continue; }
		li=s[l]; ri=s[r]; As.s.clear(); As.ans.clear(); 
		for(i=l; s[i]==s[l] && i<=r; ++i) As.s.pb(p[i]); 
		for(i=r; s[i]==s[r] && i>=l && li!=ri; --i) As.s.pb(p[i]); 
		if(li+1<=ri-1) Merge(li+1, ri-1); 
		As.build(); 
		lstans=As.que(k-1); 
//		printf("# "); 
		printf("%lld\n", lstans); 
	}
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值