题解 P4728 [HNOI2009]双递增序列

· · 题解

根据题目要求,容易发现我们得到的两个序列中是不能有任何逆序对的。考虑把原序列建成一张图,若原序列中 a_ia_j 满足:

于是题目就被转化成:把一张图分成结点数相同的两部分,每一部分内部没有边相连。 自然地考虑到二分图。若建出来的图不是二分图,则原序列一定不符合要求;若建出来的图是二分图,那么考虑将图中的每个连通块进行染色,分别记下每个连通块中被染成两种不同颜色的结点数。容易得知,要得出两个符合要求的子序列,必须存在一种方案,在每个连通块中取出一种颜色的结点,使得取出的所有结点数之和为 $\dfrac n2$。直接 dp 即可。 Code: ```cpp //sxy bless pr #include<bits/stdc++.h> using namespace std; int n,a[2005]; vector<int> e[2005]; int c[2005]; int cl[2005],now; bool f[2005][4005]; bool dfs(int x,int co){ if(c[x]) return true; c[x]=co; cl[co]++; bool flag=true; for(int i=0;i<e[x].size();i++){ if(!c[e[x][i]]){ flag=(dfs(e[x][i],now*2+1-co)&&flag); } else if(c[e[x][i]]==c[x]) return false; } return flag; } void solve(){ scanf("%d",&n); memset(c,0,sizeof(c)); memset(cl,0,sizeof(cl)); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) e[i].clear(); for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(a[i]>=a[j]){ e[i].push_back(j);e[j].push_back(i); } } } now=1; for(int i=1;i<=n;i++){ if(!c[i]){ if(!dfs(i,now)){ puts("No!"); return; } now+=2; } } memset(f,0,sizeof(f)); f[1][cl[1]]=f[1][cl[2]]=1; for(int i=3;i<=now;i+=2){ for(int j=0;j<=n;j++){ f[i][j+cl[i]]|=f[i-2][j]; f[i][j+cl[i+1]]|=f[i-2][j]; } } if(f[now][n/2]) puts("Yes!"); else puts("No!"); } int main(){ int m; cin>>m; for(int i=1;i<=m;i++) solve(); } ```