1. 程式人生 > 實用技巧 >[CF1398A-F] Codeforces Round 93

[CF1398A-F] 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;
}

CF1398F Controversial Rounds

http://codeforces.com/contest/1398/problem/F

還沒寫完,待填坑