zoukankan      html  css  js  c++  java
  • 解题报告: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
    ",a[1].sum);
    	}
    	return 0;
    }
    
  • 相关阅读:
    java 导入导出的 命令
    点击 table 单元格 取值
    SQL Server存储过程创建和修改
    js正则匹配过滤 特殊字符
    java 学习框架
    Table 表单样式
    Table 表单
    Table 固定表头的几种方法
    .Net 高效开发之不可错过的实用工具
    sql 批量插入数据到Sqlserver中 效率较高的方法
  • 原文地址:https://www.cnblogs.com/tlx-blog/p/12639521.html
Copyright © 2011-2022 走看看