正题
题目大意
长度为nnn的序列,分割成两个上升子序列要求长度差最小
解题思路
我们对于i<j,ai≥aji<j,a_i\geq a_ji<j,ai≥aj的点之间连边,然后可以对于一个联通块进行二分图染色,我们可以发现,如果我们先固定一个点的颜色,那么这个联通块的二分图染色方案数为1或0。
之后两个联通块互不影响,所以我们可以用dpdpdp来计算方案。
考虑继续优化,我们可以发现如果iii到jjj之间有连边,那么对于任意一个i<k<ji<k<ji<k<j,iii和kkk和jjj必定联通,那么我们可以推得一个联通块必定是一个区间,而一个区间的分界点满足该点前面的所有数都比后面的所有数要小。
因为划分方案数为2cnt2^{cnt}2cnt(cnt为联通块数量。所以联通块数量不超过log(1e18)log(1e18)log(1e18),可以通过本题
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N=1e5+10,K=300;
int T,n,a[N],maxs[N],mins[N];
int cnt,b[K],e[K],z[K];
bool f[2][N];
stack<int> q1,q2;
int main()
{//freopen("sample2.in","r",stdin);scanf("%d",&T);while(T--){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);maxs[i]=max(maxs[i-1],a[i]);}mins[n+1]=2147483647/3;cnt=0;for(int i=n;i>=1;i--)mins[i]=min(mins[i+1],a[i]);for(int i=1;i<=n;i++)if(maxs[i]<=mins[i+1]||i==n)b[++cnt]=e[cnt-1]+1,e[cnt]=i;bool flag=0;for(int i=1;i<=cnt;i++){while(!q1.empty())q1.pop();while(!q2.empty())q2.pop(); for(int j=b[i];j<=e[i];j++){if(q1.empty()||a[j]>q1.top())q1.push(a[j]);else if(q2.empty()||a[j]>q2.top())q2.push(a[j]);else{flag=1;break;}}if(flag)break;z[i]=q1.size()-q2.size();}if(flag){printf("-1\n");continue;}memset(f,0,sizeof(f));f[0][0]=1;for(int i=1;i<=cnt;i++)for(int j=0;j<=n;j++)f[i&1][j]=f[~i&1][abs(j-z[i])]|f[~i&1][abs(j+z[i])];for(int j=0;j<=n;j++)if(f[cnt&1][j]){printf("%d\n",j);break;}}
}