noip50

T1

直接线段树维护区间和显然不对差点就码了,然而暴力还极易暴零,找到LCA后,从 \(s,t\) 开始往LCA跳就错了,想一下发现确实是这样的,40pts->0pts。

还是菜,考虑的少了。

40pts: 暴力,注意从一个点跳到LCA,再从LCA跳下来,搞错了直接暴零。

60pts: 就是多了链的分,自己模几组就能发现,对于一条链,从 \(s\) 出发,和从 \(t\) 出发,两者复活次数是一样的,但是直接写暴力跳无法过掉链的点,容易想到用倍增来解决,设 \(die_{i,j}\) 表示点 \(i\) 复活了 \(2^{j}\) 后所到达的点,那么现在问题就是求出点 \(i\) 第一次复活所到达的点,求个树上前缀和,直接暴力往上跳即可,查询就通过 \(die\) 数组找到 \(s,t\) 分别离 \(LCA(s,t)\) 最近且血是满的即恰好复活的点,然后再判断一下这两个点形成的路径上的权值和与 \(k\) 的关系即可,这样的话就是60pts,然而数据过氵,能100pts。

100pts: 把60pts里的暴力找第一次复活所到达的点改成二分即可,复杂度 \(O(n\log n)\)

Code
#include<cstdio>
#include<algorithm>
#define MAX 200003
#define re register
#define int64_t long long
using std::upper_bound;
namespace some
{
	int n,k,q;
	int bin[MAX];
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			int w=1; s=0; char ch=getchar();
			while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
			while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
			return s*=w,*this;
		}
	}cin;
	struct graph
	{
		int next;
		int to;
		int w;
	}edge[MAX<<1];
	int cnt=1,head[MAX];
	auto add = [](int u,int v,int w) ->void { edge[++cnt] = (graph){head[u],v,w},head[u] = cnt; };
}using namespace some;
namespace TC
{
	int die[MAX][21];
	int64_t sum[MAX],dis[MAX];
	int sta[MAX],top[MAX];
	int dep[MAX],size[MAX];
	int fa[MAX][21],son[MAX];
	inline void dfs1(int u)
	{
		size[u] = 1;
		sta[++top[0]] = u,dis[top[0]] = sum[u];
		die[u][0] = sta[upper_bound(dis+1,dis+1+top[0],sum[u]-k)-dis-1];
		for(re int i=1; i<=bin[dep[u]]; i++)
		{ fa[u][i] = fa[fa[u][i-1]][i-1],die[u][i] = die[die[u][i-1]][i-1]; }
		for(re int i=head[u],v/*,p*/; i; i=edge[i].next)
		{
			v = edge[i].to;
			if(v!=fa[u][0])
			{
				sum[v] = sum[u]+edge[i].w;
				dep[v] = dep[fa[v][0] = u]+1;
				/*p = u;
				while(p)
				{
					if(sum[v]-sum[p]>=k)
					{ die[v][0] = p; break ; }
					p = fa[p][0];
				}*/
				dfs1(v);
				size[u] += size[v];
				if(!son[u]||size[v]>size[son[u]])
				{ son[u] = v; }
			}
		}
		top[0]--;
	}
	inline void dfs2(int u,int t)
	{
		top[u] = t;
		if(son[u])
		{ dfs2(son[u],t); }
		for(re int i=head[u],v; i; i=edge[i].next)
		{
			v = edge[i].to;
			if(v!=fa[u][0]&&v!=son[u])
			{ dfs2(v,v); }
		}
	}
	auto LCA = [](int a,int b) -> int
	{
		while(top[a]!=top[b])
		{
			if(dep[top[a]]>dep[top[b]])
			{ a = fa[top[a]][0]; }
			else
			{ b = fa[top[b]][0]; }
		}
		return dep[a]<dep[b]?a:b;
	};
}using namespace TC;
namespace OMA
{
	auto main = []() -> signed
	{
		//freopen("node.in","r",stdin);
		//freopen("my.out","w",stdout);
		cin >> n >> k; dep[1] = 1;
		for(re int i=2,u,v,c; i<=n; i++)
		{ cin >> u >> v >> c; add(u,v,c),add(v,u,c); bin[i] = bin[i>>1]+1; }
		dfs1(1),dfs2(1,1);
		//printf("QAQ\n");
		/*for(re int i=1; i<=n; i++)
		{
			printf("dep[%d]=%d\n",i,dep[i]);
			for(re int j=0; j<=bin[dep[i]]; j++)
			{ printf("%d ",die[i][j]); }
			printf("\n");
		}*/
		cin >> q;
		for(re int i=1,s,t,lca,ans; i<=q; i++)
		{
			cin >> s >> t;
			lca = LCA(s,t),ans = 0;
			//printf("dep[s=%d]=%d bin1=%d dep[t=%d]=%d bin2=%d lca=%d\n",s,dep[s],bin[dep[s]],t,dep[t],bin[dep[t]],lca);
			for(re int j=bin[dep[s]]; ~j; j--)
			{
				//printf("%d ",die[s][j]);
				if(dep[die[s][j]]>=dep[lca])
				{ /*printf("s: which=%d\n",j);*/ ans += 1<<j,s = die[s][j]; /*break ;*/ }
			}
			//printf("\n");
			for(re int j=bin[dep[t]]; ~j; j--)
			{
				//printf("%d ",die[t][j]);
				if(dep[die[t][j]]>=dep[lca])
				{ /*printf("t: j=%d which=%d\n",j,die[t][j]);*/ ans += 1<<j,t = die[t][j]; /*break ;*/ }
			}
			//printf("\n");
			if(sum[s]+sum[t]-2*sum[lca]>=k)
			{ /*printf("done ");*/ ans++; }
			printf("%d\n",ans);
		}
		return 0;
	};
}
signed main()
{ return OMA::main(); }

T2

读题出锅,啥分也没有,求和搞成取 \(\max\) 的估计就我一个了。

20pts: 直接 \(n^{2}\) dp即可,注意是求和,不是取 \(\max\)

100pts: 正解分治,先咕了。

T3

\(n^{2}\) 过百万,暴力碾标算!

考场上因为自己的某些神奇操作,导致我生成数据生成错了,今早重新粘了一下就生成对了.....

\(O(n^{2})\) 做法:

就是暴力.....

这个 \(n^2\) 跑不满,所以能过原数据。

逝去的100pts
#include<cstdio>
#define MAX 5000003
#define re register
typedef unsigned long long ull;
ull A,B;
int n,L,X,Y;
int l[MAX],r[MAX];
int lpre[MAX],rpre[MAX];
const int p = 998244353;
namespace some
{
	auto swap = [](int &a,int &b) ->void { int t=a; a=b; b=t; };
	auto xorshift = []()
	{
		ull T = A, S = B;
		A = S;
		T ^= T<<23,T ^= T>>17,T ^= S^(S>>26);
		B = T;
		return T+S;
	};
	auto make_data = []() -> void
	{
		for(int i=1; i<=n; i++)
		{
			l[i] = xorshift()%L+X,r[i] = xorshift()%L+Y;
			if(l[i]>r[i]){ swap(l[i],r[i]); }
			lpre[i] = l[i],rpre[i] = r[i];
			//{ printf("%d %d\n",l[i],r[i]); }
		}
	};
	auto max = [](int a,int b) -> int { return a>b?a:b; };
	auto min = [](int a,int b) -> int { return a<b?a:b; };
	auto mod = [](int a,int b) -> int { return b-p+a>=0?b-p+a:a+b; };
}using namespace some;
namespace OMA
{
	int f[MAX],rec,cnt,ans;
	auto update = [](int i) -> void { lpre[i] = l[i],rpre[i] = r[i]; };
	auto main = []() -> signed
	{
		scanf("%d%d%d%d%lld%lld",&n,&L,&X,&Y,&A,&B); make_data();
		f[1] = f[n] = 1,r[1] = r[n] = 0;
		while(++rec)
		{
			for(re int i=2; i<=n-1; i++)
			{
				if(!r[i])
				{ /*printf("1: rec=%d %d\n",rec,i);*/ update(i-1); continue ; }
				if(!rpre[i-1]||!rpre[i+1])
				{ /*printf("2: rec=%d %d\n",rec,i);*/ update(i-1),r[i] = 0,f[i] = rec; continue ; }
				l[i] = max(l[i]+1,max(lpre[i-1],lpre[i+1]));
				r[i] = min(r[i]-1,min(rpre[i-1],rpre[i+1]));
				//printf("id=%d l=%d r=%d\n",i,l[i],r[i]);
				if(l[i]>r[i])
				{ r[i] = 0,f[i] = rec; }
				//printf("3: rec=%d %d\n",rec,i);
				update(i-1);
			}
			update(n-1),update(n),cnt = 0;
			for(re int i=1; i<=n; i++)
			{ cnt += !r[i]; }
			if(cnt==n)
			{ break ; }
		}
		for(re int i=1,j=1; i<=n; i++,j=1LL*j*3%p)
		{ /*printf("f[%d]=%d\n",i,f[i]);*/ ans = mod(ans,1LL*f[i]*j%p); }
		printf("%d\n",ans);
		return 0;
	};
}
signed main()
{ return OMA::main(); }

题解做法:

image

并不会

upd on 09-10 night

下午刚氵过,晚上就被战神卡了QAQ,研究正解去了,可能会填上.....

posted @ 2021-09-09 21:00  -OMA-  阅读(61)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end