11.5又是三道水题

本文通过三道算法题目,探讨了在解决数学问题和博弈论问题时的策略。第一题涉及二次方程组的解法,通过数学变换找到递推规律;第二题介绍了博弈论中的阶梯博弈,解释了奇数堆Nim和的胜负策略;第三题讨论了树上距离的最大值问题,利用树链剖分和线段树优化求解。通过对这些题目的深入分析,揭示了数学思维和博弈论在算法竞赛中的重要性。
摘要由CSDN通过智能技术生成

水题(指三道题debug了接近一天),T4摆烂了

T1初中数学题

大意

∏(ai^2+bi^2)=x^2+y^2,输入正整数ai,bi,输出正整数x,y

思路

直接把答案算出来后暴力枚举肯定是不行的,所以ai和bi能对我们的答案有提示。尝试思考数学方法。当n=2时如何求解。化简

$$
原式=a1^2a2^2+a1^2b2^2+b1^2a2^2+b1^2b2^2 =(a1a2+b1b2)^2+(a1b2-b1a2)^2
$$

这样(a1^2+b1^2)(a2^2+b2^2)=x^2+y^2 可求出一组x,y。当n>=2时,把每次求出的x,y作为a1,a2递推就行了。

T2你不会的博弈论

大意

一开始他们有一个长度为 的序列 n的序列满足不严格单调递增 。现在异客和史尔特尔将要轮流进要轮流进行操作,每次操作可以选中一个数并将其减去一个正整数,操作完之后序列必须还要满足上述性质。由于异客获得了人民的力量,他获得了 q次修改序列的权利,具体来说,他每次可以在序列前面或后面加入一个数字(加入后保证仍然满足上述性质)。那么先手胜还是后手胜?

思路

先求差分。更改一个数之后只会改变差分数组里的两个值,且改变量的绝对值相等。这个问题就转化为阶梯博弈(Nim博弈的变式),即有n堆石子,我们可以从第i堆石子取若干个移动到第i-1堆里,第一堆的石子被取走后就不再出现在游戏中。当无石子可取(即第一堆无石子)时,失败。

结论:当奇数堆的Nim和为0时必败,大于0时必胜。

证明:

若石子都被取完,奇数堆Nim和=0

若奇数堆Nim和=0,则一次操作后,Nim和一定不为0

若奇数堆Nim和!=0,则一次操作后,可保证Nim和为0

动态维护奇数堆Nim和即可。

T3 简单的树上距离

大意

给定一棵 n个节点的边权全部为正整数的树以及q次询问,每次询问给出四个参数l1,r2,l2,r2 ,满足 。对于每次询问,设点集A为所有编号在[l1,r1]内的点,B为所有编号在[l2,r2]内的点。求从点集A和B中各选一个点,能形成的最大距离。T=10,1<=n,q<=3e3

思路

树链剖分(求 LCA)。

结论:若点集A中a1,b1,两点形成最大距离,点集B中a2,b2,两点形成最大距离,则点集A,B中各选一个点形成最大距离一定是a1,b1,a2,b2以这四个数中的两个数为端点。(感性理解)。

用线段树维护一个区间[l,r]点集中的最大距离,保存这个距离的两端点,push up时最大距离的候选项有6个:x.dat;y.dat,juli(x.F,y.F),juli(x.F,y.S),juli(x.S,y.F),juli(x.S,y.S);

我写的BUG(DE了将近一天)

  1. 建双向边,数组大小没开N<<1

  2. 树链剖分写挂了(写成了点剖分)

  3. ask函数中,当只递归一个儿子,x,y只有一个有值时,这时merge(x,y)就会出错,可能merge出来的是没有值的。

  4. 对于每一次询问,ask(l1,r1),ask(l2,r2),得到的答案不能merge,因为merge函数求的是整个点集的最大距离,而询问的是两个点集各选一个点形成的最大距离。

#include<bits/stdc++.h>
using namespace std;
#define fuck puts("fuck");
#define int long long
const int MAXN=1e5+10;
int _,n,q;
int tot,fa[MAXN],size[MAXN],son[MAXN],top[MAXN],dep[MAXN],dis[MAXN];
int ccf,head[MAXN],nxt[MAXN<<1],to[MAXN<<1],val[MAXN<<1];

template<typename W>inline void rd(W &x){
	W ch=getchar(),tx=1;x=0;
	while(!isdigit(ch))	tx=ch=='-'?-1:tx,ch=getchar();
	while(isdigit(ch))	x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	x=tx*x;
}

void dfs1(int x,int dad){
	size[x]=1;
	dep[x]=dep[fa[x]=dad]+1; 
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y!=dad){
			dis[y]=dis[x]+val[i];
			dep[y]=dep[x]+1;
			dfs1(y,x);
			if(size[y]>size[son[x]])
				son[x]=y;
			size[x]+=size[y];
		
		}
	}
}

void dfs2(int x){
	if(son[fa[x]]==x)
		top[x]=top[fa[x]];
	else top[x]=x;
	if(son[x])
		dfs2(son[x]);
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y!=fa[x]&&y!=son[x])
			dfs2(y);
	}
}

inline int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])
		swap(x,y);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])
		swap(x,y);
	return y;
}//树剖LCA  

inline int juli(int x,int y){
	return dis[x]+dis[y]-2*dis[LCA(x,y)];
}

struct node{int F,S,dat;};

class Seg_Tre{
	private:
		node a[MAXN<<2];
		int L[MAXN<<2],R[MAXN<<2];
		#define lid id<<1
		#define rid id<<1|1
	public:
		inline node merge(node x,node y){
			node ans;
			int j1,j2,j3,j4;
			if(x.F==0)	return y;
			if(y.F==0)	return x;//只有一个有值的时候merge个屁 
			ans=(x.dat>y.dat)?x:y;
			j1=juli(x.F,y.F);	j2=juli(x.F,y.S);
			j3=juli(x.S,y.F);	j4=juli(x.S,y.S);
			if(ans.dat<j1)	ans={x.F,y.F,j1};
			if(ans.dat<j2)	ans={x.F,y.S,j2};
			if(ans.dat<j3)	ans={x.S,y.F,j3};
			if(ans.dat<j4)	ans={x.S,y.S,j4};
			return ans;
		}
		void build(int id,int l,int r){
			L[id]=l,R[id]=r;
			if(l==r){
				a[id].F=a[id].S=l;
				a[id].dat=0;
				return;
			}
			int mid=l+r>>1;
			build(lid,l,mid);
			build(rid,mid+1,r);
			a[id]=merge(a[lid],a[rid]);
			return;
		}
		node ask(int id,int l,int r){
			if(l<=L[id]&&r>=R[id])
				return a[id];
			int mid=L[id]+R[id]>>1;
			node x=(node){0,0,0},y=(node){0,0,0};//
			if(l<=mid)
				x=ask(lid,l,r);
			if(r>mid)
				y=ask(rid,l,r);
			return merge(x,y);
		}
}tree;

inline void add(int u,int v,int w){
	to[++ccf]=v;
	val[ccf]=w;
	nxt[ccf]=head[u];
	head[u]=ccf;
}

inline void init(){
	rd(n),rd(q);
	int u,v,w;
	for(int i=1;i<n;++i){
		rd(u),rd(v),rd(w);
		add(u,v,w);add(v,u,w);
	}
	dfs1(1,0);
	dfs2(1);
	tree.build(1,1,n);
}

inline void work(){
	int l1,r1,l2,r2;
	node x,y,ans;
	while(q--){
		rd(l1),rd(r1),rd(l2),rd(r2);
		x=tree.ask(1,l1,r1);
		y=tree.ask(1,l2,r2);
		int j1,j2,j3,j4;
		j1=juli(x.F,y.F);	j2=juli(x.F,y.S);
		j3=juli(x.S,y.F);	j4=juli(x.S,y.S);//不能merge 
		ans={x.F,y.F,j1};
		if(ans.dat<j2)	ans={x.F,y.S,j2};
		if(ans.dat<j3)	ans={x.S,y.F,j3};
		if(ans.dat<j4)	ans={x.S,y.S,j4};
		printf("%lld\n",ans.dat);
	}
}

signed main(){
	freopen("wanted.in","r",stdin);
	freopen("wanted.out","w",stdout);
	init();
	work();
	return !~(0^_^0);
}
/*
5 6
4 2 4
2 1 3
4 3 6
2 5 1
1 2 3 4
1 1 2 3
1 1 2 5
2 3 4 5
4 4 5 5
1 2 4 5
*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值