AGC 048 A~D题解

89 篇文章 1 订阅

AGC 048 A~D

作者赛况:

RankScoreABCD
981700(-1)(86:11)300(9:05)700(40:24)700(-1)(81:11)(-4)

A

设t=“atcoder”
分以下几种情况讨论:

  • s>t,则答案=0
  • s[1] ≠ ′ a ′ \neq'a' =a,答案=1
  • s[0]=s[1]=‘a’,则如果要使得s>t,必须要将某一个字符移动到s[0]或s[1]的位置,如果字符c ≠ ′ a ′ \neq'a' =a,可以移动到s[0],如果c>‘t’,可以移动到s[1]
    O(n),找一下即可:
/*
{
######################
#       Author       #
#        Gary        #
#        2020        #
######################
*/
//#pragma GCC target ("avx2")
//#pragma GCC optimization ("O3")
//#pragma GCC optimization ("unroll-loops")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int main(){
	int t;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		if(s>"atcoder"){
			cout<<0<<endl;
		}
		else{
			bool ok=1;
			int rest=INF;
			rb(i,1,s.length()-1){
				if(s[i]!='a'){
					ok=0;
					check_min(rest,i);
					if(s[i]>'t'){
						check_min(rest,i-1);
					}
				}
			}
			if(ok){
				cout<<-1<<endl;
			}
			else cout<<rest<<endl;
		}
	}
	return 0;
}
/** 程序框架:
  *
  *
  *
  *
  **/


B

即找出某些位置 i 1 , i 2 . . . i k i_1,i_2...i_k i1,i2...ik,使得 ∑ j = 1 k B i j − A i j \sum_{j=1}^k B_{i_j}-A_{i_j} j=1kBijAij尽可能小。

那什么样的 i i i序列是合法的呢?
充要条件:对于所有的 [ ] [] []括号对,他们之间的位置个数为偶数。也就是如果 i l i_l il放’[’, i r i_r ir放’]’, i l ≠ i r ( m o d    2 ) i_l\neq i_r(\mod 2) il=ir(mod2)

则每一次我们可以从奇数位置取出一个最大的,偶数位置取出一个最大的,直到他们的和$\leq$0就行了。

/*
{
######################
#       Author       #
#        Gary        #
#        2020        #
######################
*/
//#pragma GCC target ("avx2")
//#pragma GCC optimization ("O3")
//#pragma GCC optimization ("unroll-loops")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n;
vector<int> v[2];
LL rest=0;
int a[100000+20],b[100000+20];
int main(){
	cin>>n;
	rb(i,1,n)
	{
		cin>>a[i];
		rest+=a[i];
	}
	rb(i,1,n)
	{
		cin>>b[i];
		v[i&1].PB(b[i]-a[i]);
	}
	sort(ALL(v[0]));
	sort(ALL(v[1]));
	LL MAX=0;
	LL sum=0; 
	reverse(ALL(v[0]));
	reverse(ALL(v[1]));
	rep(i,n/2){
		sum+=v[0][i]+v[1][i];
		check_max(MAX,sum);
	}
	rest+=MAX;
	cout<<rest<<endl;
	return 0;
}
/** 程序框架:
  *
  *
  *
  *
  **/


C

可以发现一个企鹅 i i i可以移动到的位置只可能是 A j + ( i − j ) A_j+(i-j) Aj+(ij),所以只要随便处理一下就行了。

可以在脑子里想一想移动的过程。真的不太好描述。

推荐一个相当方的做法

/*
{
######################
#       Author       #
#        Gary        #
#        2020        #
######################
*/
//#pragma GCC target ("avx2")
//#pragma GCC optimization ("O3")
//#pragma GCC optimization ("unroll-loops")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n,L;
int a[100000+20],b[100000+20];
int nearl[100000+20],nearr[100000+20],l[100000+20],r[100000+20];
map<int,int> app;
int main(){
	cin>>n>>L;
	rb(i,2,n+1)
		cin>>a[i];
	rb(i,2,n+1)
		cin>>b[i]; 
	a[1]=b[1]=0;
	a[n+2]=b[n+2]=L+1; 
	n+=2;
	int delta=0;
	rb(i,1,n){
		delta++;
		if(app[b[i]-delta]){
			l[i]=app[b[i]-delta];
		}
		app[a[i]-delta]=i;
	}
	delta=0;
	app.clear();
	rl(i,n,1){
		delta--;
		if(app[b[i]-delta]){
			r[i]=app[b[i]-delta];
		}
		app[a[i]-delta]=i;
	}
	rb(i,1,n){
		nearl[i]=1;
		if(b[i]==b[i-1]+1){
			nearl[i]+=nearl[i-1];
		}
	}
	rl(i,n,1){
		nearr[i]=1;
		if(b[i]==b[i+1]-1){
			nearr[i]+=nearr[i+1];
		}
	}
	LL rest=0;
	rl(i,n,1){
		if(b[i]==a[i]) continue;
		if(l[i]==false&&r[i]==false){
			cout<<"-1\n";
			return 0;
		}		
		if(l[i]){
			assert(!r[i]);
			rest+=i-l[i];
			i-=min(i-l[i]-1,nearl[i]-1);
			continue;	
		} 
	}
//	cout<<rest<<endl;
	rb(i,1,n){
		if(b[i]==a[i]) continue;
		if(r[i]){
			assert(!l[i]);
			rest+=r[i]-i;
			i+=min(r[i]-i-1,nearr[i]-1);
			continue;	
		} 
	}
	cout<<rest<<endl;
	return 0;
}
/** 程序框架:
  *
  *
  *
  *
  **/


D

非常巧妙的dp。

赛场上我还以为有一个毒瘤的结论,猜了好多遍都过不了。。。

对于一段区间 [ l , r ] [l,r] [l,r],设先手是拿左边的,保证必胜, a l a_l al最小为 L e f t l , r Left_{l,r} Leftl,r,同理先手拿右边的, a r a_r ar最小为 R i g h t l , r Right_{l,r} Rightl,r

考虑转移 L e f t l , r Left_{l,r} Leftl,r

l = r l=r l=r,则 L e f t l , r = 1 Left_{l,r}=1 Leftl,r=1

R i g h t l + 1 , r > a r Right_{l+1,r}>a_r Rightl+1,r>ar,则 L e f t l , r = 1 Left_{l,r}=1 Leftl,r=1。(直接拿完则必胜)

否则, a r ≥ R i g h t l + 1 , r , a_r\geq Right_{l+1,r}, arRightl+1,r,

  • 如果 a r a_r ar一旦 = R i g h t l + 1 , r =Right_{l+1,r} =Rightl+1,r,就必须一次拿完,不然变成 a r − 1 a_r-1 ar1这时候 l l l拿完了最左边一堆, R i g h t Right Right就必败了。

  • 如果 a r > R i g h t l + 1 , r a_r>Right_{l+1,r} ar>Rightl+1,r就与 L e f t Left Left互相消耗。这样可以在 a r a_r ar减少的同时减少 a l a_l al

  • a r > R i g h t l + 1 , r a_r>Right_{l+1,r} ar>Rightl+1,r时, L e f t Left Left也一定不会直接拿完,不然 R i g h t Right Right必胜,肯定也是一次拿一个。

  • a r = R i g h t l + 1 , r a_r=Right_{l+1,r} ar=Rightl+1,r的时候, a l a_l al一定已经消耗了 a r − R i g h t l + 1 , r + 1 a_r-Right_{l+1,r}+1 arRightl+1,r+1个,这时候 a r a_r ar一次拿完, a l a_l al剩下的一定也要 ≥ L e f t l , r − 1 \geq Left_{l,r-1} Leftl,r1.

综上 L e f t l , r Left_{l,r} Leftl,r的转移为:

L e f t l , r Left_{l,r} Leftl,r
= 1 ( l = r ) =1(l=r) =1(l=r)
= 1 ( R i g h t l + 1 , r > a r ) =1(Right_{l+1,r}>a_r) =1(Rightl+1,r>ar)
= a r − R i g h t l + 1 , r + 1 + L e f t l , r − 1 ( a r ≥ R i g h t l + 1 , r ) =a_r-Right_{l+1,r}+1+Left_{l,r-1}(a_r\geq Right_{l+1,r}) =arRightl+1,r+1+Leftl,r1(arRightl+1,r)
R i g h t Right Right的也与其类似,就不写了。

/*
{
######################
#       Author       #
#        Gary        #
#        2020        #
######################
*/
//#pragma GCC target ("avx2")
//#pragma GCC optimization ("O3")
//#pragma GCC optimization ("unroll-loops")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
#define left Left
#define right Right
#define int LL
/*}
*/
int N,a[101];
int left[101][101],right[101][101];
string s[2]={"Second","First"};
signed main(){
	fastio;
	int T;
	cin>>T;
	while(T--){
		cin>>N;
		rb(i,1,N)
			cin>>a[i];
		rb(len,1,N)
			rb(i,1,N-len+1)
			{
				int j=i+len-1;
				if(j==i){
					left[i][j]=right[i][j]=1;
					continue;
				}
				if(j==i+1){
					left[i][j]=a[j]+1;
					right[i][j]=a[i]+1;
					continue;
				}
				if(left[i][j-1]>a[i]){
					right[i][j]=1;
				}
				else{
					right[i][j]=right[i+1][j]+(a[i]-left[i][j-1]+1);
				}
				if(right[i+1][j]>a[j]){
					left[i][j]=1;
				}
				else{
					left[i][j]=left[i][j-1]+(a[j]-right[i+1][j]+1);
				}
			}
		cout<<s[(left[1][N]<=a[1])]<<endl;
	}
	return 0;
}
/** 程序框架:
  *
  *
  *
  *
  **/


AGC真的太强了,前四题没有一题代码过50行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值