【学习笔记】AGC058

Adjacent Chmax

我会转化!!

怎么设计 d p dp dp状态??

合法的最终序列 { a i } \{a_i\} {ai}满足如下条件:

1.1 1.1 1.1对于任意 a i a_i ai满足 L a i ≤ i ≤ R a i L_{a_i}\le i\le R_{a_i} LaiiRai,其中 [ L i , R i ] [L_i,R_i] [Li,Ri]表示 i i i能操作的最大区间
1.2 1.2 1.2 r k i rk_i rki表示 i i i在原序列中的位置。对于任意 a i a_i ai满足 r k a i ≥ r k a i − 1 rk_{a_i}\ge rk_{a_{i-1}} rkairkai1

d p [ i ] [ j ] dp[i][j] dp[i][j]表示覆盖了 [ 1 , i ] [1,i] [1,i],并且 a [ i ] = j a[i]=j a[i]=j的方案数

那么 d p [ i ] [ j ] = [ L j ≤ i ≤ R j ] ∑ r k k ≤ r k j d p [ i − 1 ] [ k ] dp[i][j]=[L_j\le i\le R_j]\sum_{rk_k\le rk_j}dp[i-1][k] dp[i][j]=[LjiRj]rkkrkjdp[i1][k]

Planar Tree

考虑从一个位置断开,那么合法的方案满足:

1.1 1.1 1.1 任意两个点之间是联通的,并且对于任意两条线段,满足包含或不相交的关系。

直接做非常棘手,但是注意到每个点的数字最多只有 4 4 4种情况,这启发我们通过讨论简化情况。

如果两个相邻位置上的数相同,那么可以缩成一个点,因为它们总是连向同一个节点。

如果一个位置是 1 / 4 1/4 1/4,并且这个点相邻的数字恰好相差 1 1 1,那么我们可以把这个点删去,表示连了一条长度为 1 1 1的线段。

然后,我们可以递归地构造这棵树,即圆上一定存在两个相邻的差恰好为 1 1 1的点,把它们连起来,并删除掉其中一个节点,直到圆上只剩一个节点为止。

那么我们可以想到,将环从一个位置断开,然后从前往后扫一遍就能得到答案。

并且我们观察到,一个 1 1 1恰好会抵消掉一个 3 3 3,并且一个 4 4 4恰好会抵消掉一个 2 2 2,最后剩下 2 , 3 2,3 2,3交错的序列并不影响答案。因此我们通过比较 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4的数目就能得出答案。

那么为什么出现 ( 1 , 2 ) (1,2) (1,2), ( 3 , 4 ) (3,4) (3,4)时要将靠边界的那个值删掉呢?道理很简单,从后面的过程中我们能看出尽量保留 2 , 3 2,3 2,3是更优的,因为即使剩余的 2 , 3 2,3 2,3多余了也能互相消去。而对于 1 , 4 1,4 1,4会抵消掉部分 2 , 3 2,3 2,3,显然我们应该最大化 2 , 3 2,3 2,3的数量而最小化 1 , 4 1,4 1,4的数量。

复杂度 O ( n ) O(n) O(n)。细节在于,如果我们把 1 , 4 1,4 1,4看成 I I I类点, 2 , 3 2,3 2,3看成 I I II II类点,那么每次相当于在 I I I, I I II II类点中各删除一个,因此限制为 c n t 1 + c n t 4 < c n t 2 + c n t 3 cnt_1+cnt_4<cnt_2+cnt_3 cnt1+cnt4<cnt2+cnt3。反之如果满足这个条件,那么每次一定能找到两个数进行抵消。

#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
int T,n,m,cnt,a[300005],b[300005],c[300005];
int solve(){
	cin>>n,m=cnt=0;
	for(int i=1;i<=n;i++){
		int x;cin>>x;
		if(b[m]==x)continue;
		else if(b[m]==4&&x==3||b[m]==1&&x==2)b[m]=x;
		else if(!(b[m]==2&&x==1||b[m]==3&&x==4))b[++m]=x;
	}
	int l=1,r=m;
	while(r>l){
		if(b[l]==b[r])r--;
		else if(b[l]==1&&b[r]==2||b[l]==4&&b[r]==3)l++;
		else if(b[r]==1&&b[l]==2||b[r]==4&&b[l]==3)r--;
		else break;
	}int f[5]={};
	for(int i=l;i<=r;i++){
		f[b[i]]++;
	}
	if(f[1]+f[4]<f[3]+f[2]){
		return 1;
	}return 0;
}
int main(){
	cin>>T;
	while(T--){
		cout<<(solve()?"Yes":"No")<<"\n";
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值