zoukankan      html  css  js  c++  java
  • 洛谷 P6619

    洛谷题目页面传送门

    有两种战士:冰和火,每个战士都有自身温度和能量。冰战士能参战当且仅当自身温度不大于场地温度,火战士相反。对于一个确定的场地温度,双方所有能参战的战士互相消耗相等的能量直到某一方没有能量了。你需要支持(2)(q)次操作:

    1. ( exttt1 x y z):一个战士报名,若(x=0)是冰战士否则是火战士,自身温度为(y),能量为(z)(yinleft[1,2 imes10^9 ight])
    2. ( exttt2 x):撤销第(x)次操作,保证第(x)次操作是报名操作。

    每次操作后,你需要选择场地温度来最大化双方消耗的总能量,在最大化总能量的前提下最大化场地温度,输出这两个值。若无法消耗能量,输出( exttt{Peace})

    (qinleft[1,2 imes10^6 ight])

    设当前共有(n)个冰战士,第(i)个自身温度为(a_i),能量为(b_i);共有(m)个火战士,第(i)个自身温度为(c_i),能量为(d_i)。那么对于场地温度(e),冰战士的总能量为(sumlimits_{i=1}^n[a_ileq e]b_i),火战士的总能量为(sumlimits_{i=1}^m[c_igeq e]d_i),那么消耗的总能量显然是:

    [2min!left(sumlimits_{i=1}^n[a_ileq e]b_i,sumlimits_{i=1}^m[c_igeq e]d_i ight) ]

    显然,冰战士总能量关于(e)单调递增,火战士总能量关于(e)单调递减,那么这两个函数的(min)就是非严格单峰的,这两个函数的差(冰减火)是单调递增的,在峰左边全负,在峰右边全正。

    那么就很容易找峰了,只需要二分出最右一个差为负的位置(x)即可。于是可以基于场地温度(离散化后的,这个离散化之后一个点代表原来值域上朝右的一个区间,所以要把所有(y)(y+1)压到离散化序列里)建一棵线段树,加/减战士时区间增加,找峰线段树二分,算答案时注意到峰左边(min)取冰,右边(min)取火,两边区间查最大值再取(max)即可。

    以上是我在考场上的做法。很显然,这个做法常数非常大,又是线段树又是懒标记,操作次数又多,再加上(2 imes10^6)的数据范围,于是我被傻逼的CCF卡常成(60mathrm{pts})了(这里再吐槽一句,LOJ上是可以过的,CCF是甚么垃圾所谓的少爷机连洛谷都跑不过)。考虑优化。

    一个显然的优化是,对于找到的峰(x),不用两边区间查最大值,因为最大答案只可能在(x)(x+1)处。但是有个问题,这并不能保证右边能最大化场地温度,这样再线段树二分即可。

    然后不难想到线段树转BIT。注意到BIT不能区间修改,于是可以转差分,区间修改就变成差分数组的双点修改,单点查值就是差分数组的前缀和。那么如何在BIT上1log二分呢?注意到,线段树上的二分并不需要单调性,而这里需要二分于上的火函数和差函数都是单调的,相当于是个多余条件?暗示了我们可以转到BIT上。

    这是一个很巧妙的trick:BIT倍增。解决的是这样的问题:求最右边的前缀和【某个关系】某个常数的那一处,其中前缀和数组与【某个关系】同方向单调(恰与本题吻合)。考虑二分转倍增,从左往右逼近(不能从右往左,那样就要反过来建,在这题上恰合适),从大到小枚举(2)的指数,可以发现从当前位置到当前位置加上当前枚举到的(2)的幂的位置中间那一段的和恰好是被记录的,因为(2)的指数是从大到小枚举的,所以中间那一段的长度一定是右端点的(mathrm{lowbit})。然后就可以正常倍增了。over。

    现在加个快读快写开个洛谷自带O2即可AC。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    char _buf[10000000],*_st=_buf,*_ed=_buf;
    #define getchar() (_st==_ed&&(_ed=(_st=_buf)+fread(_buf,1,1<<22,stdin),_st==_ed)?EOF:*_st++)
    void read(int &x){//快读 
    	x=0;char c=getchar();
    	while(!isdigit(c))c=getchar();
    	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    void prt(int x){//快写 
    	if(x>=10)prt(x/10);
    	putchar(x%10^48);
    }
    const int lowbit(int x){return x&-x;}
    const int QU=2000000;
    int qu;
    struct query{int tp,x,y,z;}qry[QU+1];
    vector<int> nums;
    void discrete(){//离散化 
    	sort(nums.begin(),nums.end());
    	nums.resize(unique(nums.begin(),nums.end())-nums.begin());
    	for(int i=1;i<=qu;i++)qry[i].y=lower_bound(nums.begin(),nums.end(),qry[i].y)-nums.begin()+1;
    }
    struct bitree{//BIT
    	int sum0[2*QU+1]/*冰*/,sum1[2*QU+1]/*火*/;
    	void init(){
    		memset(sum0,0,sizeof(sum0));memset(sum1,0,sizeof(sum1));
    	}
    	void add0(int x,int v){//加冰 
    		while(x<=nums.size())sum0[x]+=v,x+=lowbit(x);
    	}
    	void add1(int x,int v){//加火 
    		while(x<=nums.size())sum1[x]+=v,x+=lowbit(x);
    	}
    	int lasne(){//最右的差为负的一处 
    		int ans=0;int now=0;
    		for(int i=22;~i;i--)if(ans+(1<<i)<=nums.size()&&now+sum0[ans+(1<<i)]-sum1[ans+(1<<i)]<0)ans+=1<<i,now+=sum0[ans]-sum1[ans];//倍增 
    		return ans;
    	}
    	int laseq(int v){//最右的火函数等于v的一处,同上 
    		int ans=0;int now=0;
    		for(int i=22;~i;i--)if(ans+(1<<i)<=nums.size()&&now+sum1[ans+(1<<i)]>=v)ans+=1<<i,now+=sum1[ans];
    		return ans;
    	}
    	int Sum0(int x){//冰,单点查值->前缀和 
    		int res=0;
    		while(x)res+=sum0[x],x-=lowbit(x);
    		return res;
    	}
    	int Sum1(int x){//火,同上 
    		int res=0;
    		while(x)res+=sum1[x],x-=lowbit(x);
    		return res;
    	}
    }bit;
    int main(){
    	read(qu);
    	for(int i=1;i<=qu;i++){
    		read(qry[i].tp);read(qry[i].x);
    		if(qry[i].tp==1)read(qry[i].y),read(qry[i].z);
    		else qry[i].y=qry[qry[i].x].y,qry[i].z=qry[qry[i].x].z,qry[i].x=qry[qry[i].x].x;
    		nums.pb(qry[i].y);nums.pb(qry[i].y+1);//y和y+1都要pb进去 
    	}
    	discrete();
    	for(int i=1;i<=qu;i++){
    		int tp=qry[i].tp,x=qry[i].x,y=qry[i].y,z=qry[i].z;
    		if(x==0)bit.add0(y,tp==1?z:-z);//差分 
    		else bit.add1(1,tp==1?z:-z),bit.add1(y+1,tp==1?-z:z);//差分 
    		int lasne=bit.lasne(),ans1=0,ans2=0;
    		ans1=bit.Sum0(lasne);//找峰 
    		if(lasne+1<=nums.size())ans2=bit.Sum1(lasne+1);//右边也要考虑一下 
    		if(!ans1&&!ans2)puts("Peace"); 
    		else if(ans2>=ans1)prt(nums[bit.laseq(ans2)]-1/*右边的最右处*/),putchar(' '),prt(ans2<<1),putchar('
    ');
    		else prt(nums[lasne]-1),putchar(' '),prt(ans1<<1),putchar('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    SQL手工注入方法
    Python + Django 网站平台搭建之- 初识 (一)
    最新版Idea2019.3.4/2020.1完美破解
    使用 Guns 自动生成 SpringBoot + LayUI 的后台管理系统
    SpringBoot+Layui后台管理系统
    国内Maven中央仓库推荐 速度最快最好的Maven仓
    git 下载失败 中断了 继续下 怎么配置参数
    mysql 创建与授权
    jboot-admin
    自动生文器
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P6619.html
Copyright © 2011-2022 走看看