……

解题报告:luogu P4879

题目链接:P4879 ycz的妹子
操作很多,但都是吓人的。
一眼线段树,我不会告诉你我是搜线段树标签找到这题的
考虑用每个节点来维护每个城市中妹子的情况,显然只需维护:妹子的颜值与此城市是否有热恋中的妹子。
开始想维护两个线段树,经过冷静后发现在一棵线段树上搞即可。
看数据范围,知道要注意\(long\;long\)
来看基本操作(应该都是小儿科的了):
\(1\).更新及建树:

void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum,a[k].cnt=a[k<<1].cnt+a[k<<1|1].cnt;}
void build(int k,int l,int r,int x)
{
	a[k].l=l,a[k].r=r;
	if(l==r)
	{
		if(l<=x) a[k].cnt++;
		a[k].sum=(ll)t[l];
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid,x),build(k<<1|1,mid+1,r,x);
	update(k);
}

\(2\).把一个城市妹子颜值降低:

void turn(int k,int x,int y)
{
	if(a[k].l==a[k].r){a[k].sum-=(ll)y;return;}
	int mid=(a[k].l+a[k].r)>>1;
	if(x<=mid) turn(k<<1,x,y);
	else turn(k<<1|1,x,y);
	update(k);
}

\(3\).在一个城市上加一个妹子,注意把有妹子的标记打上:

void turn_to(int k,int x,int y)
{
	if(a[k].l==a[k].r){a[k].sum=(ll)y,a[k].cnt=1;return;}
	int mid=(a[k].l+a[k].r)>>1;
	if(x<=mid) turn_to(k<<1,x,y);
	else turn_to(k<<1|1,x,y);
	update(k);
}

\(4\).寻找第\(x\)个有妹子的城市:
这时候就要用到有无妹子的计数器了,我们在线段树上每次判断是在左儿子还是在右儿子即可。和权值线段树确定第\(k\)大(小)数的思想是一样的。

int find(int k,int x)
{
	if(a[k].l==a[k].r){return a[k].l;}
	int mid=a[k<<1].cnt;
	if(x<=mid) return find(k<<1,x);
	else return find(k<<1|1,x-mid);
}

\(5\).把在某个城市的妹子踢掉。
这里的函数名十分生动:\(kick\)......
我们把颜值设成\(0\),并把标记打成“无妹子”即可。

void kick(int k,int x)
{
	if(a[k].l==a[k].r){a[k].cnt=a[k].sum=(ll)0;return;}
	int mid=(a[k].l+a[k].r)>>1;
	if(x<=mid) kick(k<<1,x);
	else kick(k<<1|1,x);
	update(k);
}

\(6\).颜值和:
由于是所有数的和,只需输出树根的总颜值即可。
需要注意的是:对于所有修改操作,记得不断更新(\(update\))。
在这里提供一个拓展题:把查询总颜值改为每次求可以得到妹子的最大颜值,这样的难度就有了提高,大家可以想一下做法。
下面给出完整代码:

\(Code\)

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define MAXN 500005
#define ll long long
int n,m,x,y,t[MAXN];
char c;
struct node
{
	int l,r,cnt;
	ll sum;	
	node(){sum=cnt=(ll)0;}
}a[MAXN<<2];
void update(int k){a[k].sum=a[k<<1].sum+a[k<<1|1].sum,a[k].cnt=a[k<<1].cnt+a[k<<1|1].cnt;}
void build(int k,int l,int r,int x)
{
	a[k].l=l,a[k].r=r;
	if(l==r)
	{
		if(l<=x) a[k].cnt++;
		a[k].sum=(ll)t[l];
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid,x),build(k<<1|1,mid+1,r,x);
	update(k);
}
void turn(int k,int x,int y)
{
	if(a[k].l==a[k].r){a[k].sum-=(ll)y;return;}
	int mid=(a[k].l+a[k].r)>>1;
	if(x<=mid) turn(k<<1,x,y);
	else turn(k<<1|1,x,y);
	update(k);
}
void turn_to(int k,int x,int y)
{
	if(a[k].l==a[k].r){a[k].sum=(ll)y,a[k].cnt=1;return;}
	int mid=(a[k].l+a[k].r)>>1;
	if(x<=mid) turn_to(k<<1,x,y);
	else turn_to(k<<1|1,x,y);
	update(k);
}
int find(int k,int x)
{
	if(a[k].l==a[k].r){return a[k].l;}
	int mid=a[k<<1].cnt;
	if(x<=mid) return find(k<<1,x);
	else return find(k<<1|1,x-mid);
}
void kick(int k,int x)
{
	if(a[k].l==a[k].r){a[k].cnt=a[k].sum=(ll)0;return;}
	int mid=(a[k].l+a[k].r)>>1;
	if(x<=mid) kick(k<<1,x);
	else kick(k<<1|1,x);
	update(k);
}
int main()
{
	#define read(x) scanf("%d",&x)
	read(n),read(m);
	for(int i=1;i<=m;i++) read(t[i]);
	build(1,1,500000,n);
	for(int i=1;i<=m;i++)
	{
		cin>>c;
		if(c=='C') read(x),read(y),turn(1,x,y);
		else if(c=='I') read(x),read(y),turn_to(1,x,y);
		else if(c=='D') read(x),y=find(1,x),kick(1,y);
		else printf("%lld\n",a[1].sum);
	}
	return 0;
}
posted @ 2020-04-05 22:21  童话镇里的星河  阅读(306)  评论(0编辑  收藏  举报