codeforces366(线段树,博弈游戏)

第一题。。。

模拟


2.


最初有k个循环,vi个顶点组成的。游戏者交替选择。彼得先。每一个人必须选择一个有至少2点的周期,并把他们分成两堆。如果一个人不能再移(所有堆都是1),那么他就输掉了了游戏。

彼得想测试一些初始周期的配置集之前。最初他在第i个测试一个空集。给出n,n次加入循环,每次测试后,彼得想知道,如果玩家开始游戏以当前的周期,谁赢了?先手赢,printf 1;后手赢 printf 2


交替进行,使用最优决策,谁能赢,不能移动即为输。

这类题似乎就是要分析问题的本质,一般从小范围出发,看看在小范围下的最优决策下谁能赢,观察他们之间的关系,推出规律

还是很灵活的。

这道题,其实无论我们用什么移动策略,目标状态一定都是1,1,1,……1 (且堆数等于总点数)的情况,每一个人可以分开一个堆,变成两个小的堆,我们不知道怎么移动,但是,我们每次移动都会多出来一个堆,而目标状态有n个堆,无论用什么移动方法,到目标状态的移动次数定了,也就是说到达目标状态时谁移动定了,结果也就出来了

官方题解:First of all, instead of cycles, imagine we have bamboos (paths). A valid move in the game is now taking a path and deleting an edge from it (to form two new paths). So, every player in his move can delete an edge in the graph (with components equal to paths). So, no matter how they play, winner is always determined by the parity of number of edges (because it decreases by 1 each time). Second player wins if and only if the number of edges is even. At first it's even (0). In a query that adds a cycle (bamboo) with an odd number of vertices, parity and so winner won't change. When a bamboo with even number of vertices (and so odd number of edges) is added, parity and so the winner will change.

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;

int n;
int main()
{
	scanf("%d",&n);
	int nume=0,numo=0,ans,x;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if (x!=1)
		{if (x&1) numo++;else nume++;}
		if ((numo&1)&&(nume&1)) ans=1;
		if ((numo&1)&&!(nume&1)) ans=2;
		if (!(numo&1)&&(nume&1)) ans=1;
		if (!(numo&1)&&!(nume&1)) ans=2;
		printf("%d\n",ans);
	}
	return 0;
}

不过比赛时,我弱渣的找了规律

第三题:

3种操作

1.插入x堆插入一个信

2.删除x堆的所有信(可能已经删了)

3。删除插入的前k个信(可能已经删了)

问每个询问后,所有堆还剩下多少信

其实这个题怎么都能做。。。我写的线段树,删除x堆的信,直接暴力删(因为加进去的只有n个,所以最多删n次)

删前k个就是在把线段树中前k个付为0,标记永久化就好,因为开始的位置不会再添加,只会删除,如果已经删除了。下次就不用管了,标记永久化了


#include<cstdio>
#include<cmath>
#include<set>
#include<algorithm>
#include<cstdlib>
using namespace std;

int n,m;
struct aa
{
	int l,r,sum,set;
}a[300005*4];
int head[300005],tot,pre[300005],rest,p;
void build(int i,int l,int r)
{
	a[i].l=l;a[i].r=r;
	if (l==r) return ;
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
}
void up(int i) {a[i].sum=a[i<<1].sum+a[i<<1|1].sum;}
void add(int i,int x)
{
	if (a[i].l==a[i].r) {a[i].sum=1;return;}
	int mid=(a[i].l+a[i].r)>>1;
	if (mid>=x) add(i<<1,x);else add(i<<1|1,x);
	up(i);
}
void del(int i,int l,int r)
{
	if (a[i].set) return;
	if (a[i].l==l&&a[i].r==r)
	{
		a[i].set=1;a[i].sum=0;
		return ;
	}
	int mid=(a[i].l+a[i].r)>>1;
	if (mid>=r) del(i<<1,l,r);
	else if (mid<l) del(i<<1|1,l,r);
	else del(i<<1,l,mid),del(i<<1|1,mid+1,r);
	up(i);
} 
void insert(int y)
{
	pre[++tot]=head[y];head[y]=tot;
	add(1,tot);
}
void read1(int y)
{
	for (;head[y];head[y]=pre[head[y]]) del(1,head[y],head[y]);
}
void read2(int y)
{
	del(1,1,y);
}
int main()
{
	scanf("%d%d",&n,&p);
	build(1,1,p);
	int x,y;
	for (int i=1;i<=p;i++)
	{
		scanf("%d%d",&x,&y);
		switch(x)
		{
			case 1:insert(y);break;
			case 2:read1(y);break;
			case 3:read2(y);break;
		}
		printf("%d\n",a[1].sum);//线段树中存未读数量 
	}
	return 0;
}
感觉有点大材小用。。感觉题解和刚开始想的splay差不多,不过那就更大材小用了



评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值