【学习笔记】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"; } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530075.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(17)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示