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}
Lai≤i≤Rai,其中
[
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}}
rkai≥rkai−1
设 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]=[Lj≤i≤Rj]∑rkk≤rkjdp[i−1][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";
}
}