杭电第九场补题 Game 平衡树维护

Game

题目链接

题目大意

杭电第九场补题 Game 平衡树维护_第1张图片
初始有n列木块,第 i 列有 a[i] 个。
有两种操作:
1 x y : 向左推第x列的从下到上第y个,(如上图所示)
2 x :查询第x列有多少个木块

题解

用平衡树维护,我用的是fhq treap (因为我只会这个,我好菜hhhh)
操作二就是找第x个值是多少,很简单。。
我这里是把树按大小分裂。第一个树L有x - 1个数,剩下的在另一棵树R里,然后把R再分裂,按1分裂,分裂成Y,Z。然后Y就只有x这个节点了。返回x的权值就好了。
操作一
往左推一下,然后设可以推动的这个区间是 l ~ r ,l 左边的第一个数是L。
于是 L这个位置的数量就加 l 位置上的数量减去y - 1就好了。
r 这个位置变成了y - 1.
其他的往左平移一个就好,
主要是这个平移怎么实现?
…因为这是fhq treap 直接把l位置上的值变为y - 1,然后合并的时候,把 l 位置跟l + 1 ~ r位置的换一下,也就是合并写成 merge(y,x) ;把 l 放在右边就好了。
注意:
有不能移动的情况:
1、 最左边的木块不能向左移动。(也就是如果分裂后左边区间的最小值大于等于y不能移动)
2、 如果 y 大于x位置的数量的时候不能移动。
我可真是菜啊。

代码:
#include
#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef unordered_set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void tempwj(){freopen("P2633_1.in","r",stdin);freopen("P2633_1.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b > 0){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}};
int lb(int x){return  x & -x;}
//friend bool operator < (Node a,Node b)   重载
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9+7;
const int maxn = 4e5+10;
const int M = 1e6 + 2;
std::mt19937 rnd(233);
struct Node
{
	int l,r;
	ll sum;
	int key;
	int siz;
	ll val;
	ll minn;
}node[maxn];
int cnt, root;
int newnode(int val)
{
	node[++cnt].val = val;
	node[cnt].minn = val;
	node[cnt].sum = val;
	node[cnt].key = rnd();
	node[cnt].siz = 1;
	return cnt;
}
void update(int no)
{
	node[no].sum = node[node[no].l].sum + node[node[no].r].sum + node[no].val;
	node[no].minn = min(node[no].val, min(node[node[no].l].minn,node[node[no].r].minn));
	node[no].siz = node[node[no].l].siz + node[node[no].r].siz + 1;
}
void split(int no,int siz,int& x,int& y)
{
	if(no == 0)
	{
		x = 0;
		y = 0;
		return;
	}
	if(node[node[no].l].siz + 1 <= siz)
	{
		x = no;
		split(node[no].r,siz - node[node[no].l].siz - 1,node[no].r,y);
	}
	else
	{
		y = no;
		split(node[no].l,siz,x,node[no].l);
	}
	update(no);
}

int merge(int x,int y)
{
	if(x == 0 || y == 0)
		return x + y;
	if(node[x].key < node[y].key)
	{
		node[x].r = merge(node[x].r,y);
		update(x);
		return x;
	}
	else
	{
		node[y].l = merge(x,node[y].l);
		update(y);
		return y;
	}
}
void split_m(int no,ll val,int& x,int& y)
{
	if(no == 0)
	{
		x = 0;
		y = 0;
		return;
	}
	if(node[no].val < val || node[node[no].r].minn < val)
	{
		x = no;
		split_m(node[no].r,val,node[no].r,y);
	}
	else
	{
		y = no;
		split_m(node[no].l,val,x,node[no].l);
	}
	update(no);
}
ll query(int pos)
{
	int x,y,z;
	split(root,pos - 1,x,y);
	split(y,1,y,z);
	ll ans = node[y].val;
	root = merge(x,merge(y,z));
	return ans;
}
ll solve(int pos,ll val)
{
	if(query(pos) < val)
		return 0;
	int x1,y1;
	split(root,pos,x1,y1);
	//把pos左边区间的全放在x1里
	if(node[x1].minn >= val)
	{
		merge(x1,y1);
		return 0;
	}
	int x2,y2;
	split_m(x1,val,x2,y2);
	//找到最右边小于val的地方的右区间放在y2里,其他的在x2里
	ll ans = node[y2].sum - 1ll * node[y2].siz * (val - 1);
	int x3,y3;
	split(x2,node[x2].siz - 1,x3,y3);
	// y3放的是要移动的最左边的这个点的左边
	int x5,y5;
	split(y2,1,x5,y5);
	// x5放的是要移动的左边的这个点
	ll s = node[x5].val - val + 1;
	node[y3].val += s;
	node[y3].sum = node[y3].val;
	node[y3].minn = node[y3].val;
	node[x5].val = val - 1;
	node[x5].sum = node[x5].val;
	node[x5].minn = node[x5].val;
	root = merge(merge(merge(x3,y3),merge(y5,x5)),y1);
	return ans;
}
int a[maxn];
void add(int val,int pos)
{
	root = merge(root,newnode(val));
}


std::vector<ll> vv;
void zxbl(int no)
{
	if(no == 0)
		return;
	zxbl(node[no].l);
	vv.pb(node[no].val);
	zxbl(node[no].r);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T -- )
	{
		node[0].minn = INF;
		cnt = 0;
		root = 0;
		int n,m;
		scanf("%d%d",&n,&m);
		for (int i = 1; i <= n; i ++ )
		{
			scanf("%d",&a[i]);
		}
		for (int i = 1; i <= n; i ++ )
		{
			add(a[i],i);
		}
		// zxbl(root);
		// printf("\n");
		while(m -- )
		{
			int f;
			scanf("%d",&f);
			if(f == 1)
			{
				int x,y;
				scanf("%d%d",&x,&y);
				printf("%lld\n",solve(x,y));
			}
			else
			{
				int x;
				scanf("%d",&x);
				printf("%lld\n",query(x));
			}
			// zxbl(root);
			// printf("\n");
		}
		zxbl(root);
		for (int  i= 0; i < vv.size(); i ++ )
		{
			if(i == 0)
				printf("%lld",vv[i]);
			else
				printf(" %lld",vv[i]);
		}
		vv.clear();
		printf("\n");
		for (int i = 1; i <= cnt; i ++ )
		{
			node[i].l = node[i].r = node[i].siz = node[i].val = node[i].sum = node[i].minn = 0;
		}
	}
}

找了好长时间的bug,把所有能改的 int 全改成了 longlong 。。。难受,
最后发现少判断了一种不能移动的情况。
我好菜,小菜鸡

你可能感兴趣的:(做题笔记)