suxxsfe

一言(ヒトコト)

[CF1398A-E] Codeforces Round 93

CF1398A Bad Triangle

http://codeforces.com/contest/1398/problem/A
给出一列排好升序的数,从中选不同的三个数,是他们组不成一个三角形
显然选第 \(1,2,n\) 个,如果这三个也能组成三角形,那么就无解

int main(){int T=read();while(T--){
	int n=read();
	LL a=read(),b=read();
	for(reg int i=3;i<n;i++) read();
	LL c=read();
	if(a+b<=c) printf("1 2 %d\n",n);
	else puts("-1");
}
	return 0;
}

CF1398B Substring Removal Game

http://codeforces.com/contest/1398/problem/B
给定一列 01 串,两个人,每次轮流选择一段连续 \(0\)\(1\),删掉,如果选的是 \(1\) 则分数加上这个连续的串的长度
每个人都最大化自己的得分,求先手得分

为了最大化得分,没人肯定是找目前最长的一串 \(1\) 来删掉
又因为 \(n\le 100\),所以直接暴力模拟每次找出最长的连续 \(1\),然后更新删掉后的串和长度即可

char s[105];
int a[205];
int main(){int T=read();while(T--){
	std::scanf("%s",s+1);
	int n=std::strlen(s+1);
	for(reg int i=1;i<=n;i++){
		a[i]=s[i]-48;
	}
	int ans=0;
	for(reg int j=1;;j^=1){
		int maxlen=0,maxpos,now=0,nowpos=0;
		for(reg int i=1;i<=n;i++){
			if(a[i]){
				if(a[i-1]) now++;
				else now=1,nowpos=i;
			}
			else{
				if(a[i-1]){
					if(maxlen<now) maxlen=now,maxpos=nowpos;
				}
			}
		}
		if(maxlen<now) maxlen=now,maxpos=nowpos;
		if(!maxlen) break;
		if(j) ans+=maxlen;
		for(reg int i=maxpos;i<=maxpos+maxlen-1;i++) a[i]=a[i+maxlen];
		n-=maxlen;
		for(reg int i=maxpos+maxlen;i<=n;i++) a[i]=a[i+maxlen];
	}
	std::memset(a,0,sizeof a);
	std::printf("%d\n",ans);
}
	return 0;
}

CF1398C Good Subarrays

http://codeforces.com/contest/1398/problem/C
给定一个长度为 \(n(n\le 10^5)\) 的整数数组 \(a\)\(a_i\in [0,9]\),求有多少个字串 \([l,r]\),满足 \(\sum_{i=l}^r=r-l+1\)

变换那个式子:\(sum_r-sum_{l-1}=r-l+1\Rightarrow sum_r-sum_l=r-l\ (l<r)\)
然后令 \(S_i=sum_i-i\),则要求就是 \(S_r=S_l\ (l<r)\)
又由于值域很小,开一个 cnt 数组存当前有多少个 \(i\) 使得 \(S_i\) 取某一值,然后每次让答案加上 \(cnt_{S_i}\) 就行

#define N 1000005
LL S[N],cnt[N],sum[N];
char s[N];
int main(){int T=read();while(T--){
	int n=read();
	std::scanf("%s",s+1);
	LL ans=0;
	cnt[0]=1;
	for(reg int i=1;i<=n;i++){
		S[i]=sum[i-1]+s[i]-48-i;
		sum[i]=sum[i-1]+s[i]-48;
		ans+=cnt[S[i]];
		cnt[S[i]]++;
	}
	for(reg int i=1;i<=n;i++) cnt[S[i]]=0,sum[i]=S[i]=0;
	std::printf("%lld\n",ans);
}
	return 0;
}

CF1398D Colored Rectangles

http://codeforces.com/contest/1398/problem/D
给出三个序列,每次选择两个不同序列中的数相乘,每个数只能选一次,可以不选,问所有乘积的和最大是多少

首先可以把他们降序排序,若每次将选出的两个数删去,则每次选都是在三个序列的其中两个中,选他们的第一个元素相乘
因为肯定要尽量让大的数和大的数相乘(或者可以考虑一个更简单化的问题来说明一下:对于 \(a\le b\le c\le d\),容易证出 \(ab+cd\ge ac+bd\ge ad+bc\)
然后就可以排序后定义 \(f(i,j,k)\) 表示三个数列分别用了 \(i,j,k\) 个,的最大乘积和
转移的方式也很容易想到,直接看代码吧,不说了

int R,G,B;
LL r[205],g[205],b[205];
LL f[205][205][205];
inline int cmp(LL a,LL b){return a>b;}
int main(){
	R=read();G=read();B=read();
	for(reg int i=1;i<=R;i++) r[i]=read();
	for(reg int i=1;i<=G;i++) g[i]=read();
	for(reg int i=1;i<=B;i++) b[i]=read();
	std::sort(r+1,r+1+R,cmp);std::sort(g+1,g+1+G,cmp);std::sort(b+1,b+1+B,cmp);
	LL ans=0;
	for(reg int i=0;i<=R;i++){
		for(reg int j=0;j<=G;j++){
			for(reg int k=0;k<=B;k++){
				if(i&&j) f[i][j][k]=f[i-1][j-1][k]+r[i]*g[j];
				if(i&&k) f[i][j][k]=std::max(f[i][j][k],f[i-1][j][k-1]+r[i]*b[k]);
				if(j&&k) f[i][j][k]=std::max(f[i][j][k],f[i][j-1][k-1]+g[j]*b[k]);
				ans=std::max(ans,f[i][j][k]);
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}

CF1398E Two Types of Spells

http://codeforces.com/contest/1398/problem/E
两种技能,每个技能都有一个数值,可以为对方造成这个数值的伤害
第二种技能还可以使下一个伤害翻倍
\(n\) 个操作,每次学会或忘掉一个技能,每个操作后回到目前最多能为对方带来多少伤害(每个技能最多一次)
原题中将第一个技能描述为 fire spell,第二个是 lightning spell,下面通过这两个名字来描述

其实这题是赛后 A 的,赛时之过了 4 个,比赛的时候写了个平衡树做法,但感觉应该会有更简单的(不然 div2E 就平衡树了?而且这场前面几题都比较简单)
但因为一些细节原因赛时还是没调出来

首先每个技能都施展,设所有技能的数值和为 \(sum\),则至少造成 \(sum\) 的伤害
那么最多有几次翻倍机会?是 \(\min(lightning\_num,num-1)\)\(num\) 为当前会的技能总数,和 \(num-1\)\(\min\) 的原因是,如果 \(num\) 个技能都是 lightning,则第一个不能翻倍,最多翻倍 \(num-1\)
我们肯定是要翻倍数值大的技能。容易想到,一般来说我们总是能使前 \(\min(lightning\_num,num-1)\) 大的技能通过调整施展顺序都被翻倍
但有一点,就是如果这些要翻倍的技能都是 lightning,则会出现问题,也就是至少有一个 lightning 技能不翻倍(总会有一个 lightning 前面没有其它的 lightning 来让它翻倍)

由于我平衡树直接复制的板子,是升序维护,节点同时还要维护一个 \(sum\) 表示左右儿子的 \(sum\) 加上自己的值的和
升序维护就是 \(2\cdot sum\) 减去前 \(n-\min(lightning\_num,num-1)\) 小的数,先判一下最小 lightning 是不是在这些数其中,如果是直接正常减,如果不是,那就减去前 \(n-\min(lightning\_num,num-1)-1\) 小的,然后再减去一个最小的 lightning
至于如何维护最小的 lightning,懒得多想了,直接用了个带删除的堆

注意删除的时候除了打上 deleted 标记(我写的替罪羊,惰性删除),还要把 \(sum\) 减去它的值,但它的值不能设为 \(0\),否则无法进行后续的比较(比如获取排名等信息的时候就要根据节点权值来考虑进入右子树还是左子树),我就是因为把删除节点的值直接设为 \(0\) 导致 RE 的

#define alpha 0.7
struct tr{
	tr *ls,*rs;
	int cnt,size;
	int deleted;
	LL val,sum;
}*null,*root,*nodes[200005],**badtag;
int node_num;
inline int isbad(tr *tree){
	return tree->ls->cnt>alpha*tree->cnt+5||tree->rs->cnt>alpha*tree->cnt+5;
}
inline void pushup(tr *tree){
	tree->sum=tree->ls->sum+tree->rs->sum;
	if(!tree->deleted) tree->sum+=tree->val;
}
void dfs(tr *tree){
	if(tree==null) return;
	dfs(tree->ls);
	if(!tree->deleted) nodes[++node_num]=tree;
	dfs(tree->rs);
	if(tree->deleted) delete tree;
}
tr *build(int l,int r){
	if(l>r) return null;
	if(l==r){
		nodes[l]->ls=nodes[l]->rs=null;
		nodes[l]->cnt=nodes[l]->size=1;
		nodes[l]->sum=nodes[l]->val;
		return nodes[l];
	}
	int mid=(l+r)>>1;
	tr *tree=nodes[mid];
	tree->ls=build(l,mid-1);tree->rs=build(mid+1,r);
	tree->cnt=tree->size=1+tree->ls->size+tree->rs->size;
	pushup(tree);
	return tree;
}
inline void rebuild(tr *&tree){
	node_num=0;
	dfs(tree);
	tree=build(1,node_num);
}
void insert(tr *&tree,LL x){
	if(tree==null){
		tree=new tr;
		tree->ls=tree->rs=null;
		tree->deleted=0;
		tree->val=tree->sum=x;
		tree->size=tree->cnt=1;
		return;
	}
	if(x>tree->val) insert(tree->rs,x);
	else insert(tree->ls,x);
	tree->size++;tree->cnt++;
	pushup(tree);
	if(isbad(tree)) badtag=&tree;
}
inline void Insert(tr *&tree,LL x){
	badtag=&null;
	insert(tree,x);
	if(badtag!=&null) rebuild(*badtag);
}
inline LL kthsum(tr *tree,int rk){
	if(rk==tree->ls->size+!tree->deleted) return tree->ls->sum+(!tree->deleted)*tree->val;
	if(rk<tree->ls->size+!tree->deleted) return kthsum(tree->ls,rk);
	return tree->ls->sum+(!tree->deleted)*tree->val+kthsum(tree->rs,rk-tree->ls->size-!tree->deleted);
}
inline int rank(tr *tree,LL x){
	reg int ans=1;
	while(tree!=null){
		if(x<=tree->val) tree=tree->ls;
		else{
			ans+=tree->ls->size+!tree->deleted;
			tree=tree->rs;
		}
	}
	return ans;
}
void erase(tr *tree,int rk){
	if(!tree->deleted&&rk==tree->ls->size+1){
		tree->deleted=1;
		tree->size--;
		tree->sum=tree->ls->sum+tree->rs->sum;//tree->val 不能置零,否则无法比较
		return;
	}
	if(rk<=tree->ls->size+!tree->deleted) erase(tree->ls,rk);
	else erase(tree->rs,rk-tree->ls->size-!tree->deleted);
	tree->size--;
	pushup(tree);
}
struct HEAP{
	std::priority_queue<LL>deleted,insert;
	inline void ins(LL x){insert.push(-x);}
	inline void del(LL x){deleted.push(-x);}
	inline LL top(){
		while(!insert.empty()&&!deleted.empty()&&deleted.top()==insert.top())
			insert.pop(),deleted.pop();
		return insert.empty()?-1:(-insert.top());
	}
}heap;
inline LL get(int n,int lightnum,LL sum){
	int num=n-std::min(lightnum,n-1);
	LL minlight=heap.top();
	if(minlight==-1) return sum;
	if(rank(root,minlight)<=num) return kthsum(root,num);
	return num==1?minlight:(minlight+kthsum(root,num-1));
}
int main(){
	root=null=new tr;
	null->size=null->cnt=0;
	null->val=null->sum=0;
	null->ls=null->rs=NULL;null->deleted=0;
	int N=read();
	int n=0,lightnum=0;
	LL sum=0;
	int op,d;
	while(N--){
		op=read();d=read();
		if(d>0){
			sum+=d;n++;
			lightnum+=op;
			if(op) heap.ins(d);
			Insert(root,d);
		}
		else{
			d=-d;n--;
			sum-=d;
			lightnum-=op;
			if(op) heap.del(d);
			erase(root,rank(root,d));
		}
		printf("%lld\n",2*sum-get(n,lightnum,sum));
	}
	return 0;
}
posted @ 2020-08-15 18:35  suxxsfe  阅读(309)  评论(0编辑  收藏  举报