bzoj 4881: [Lydsy2017年5月月赛]线段游戏 树状数组+set

来源:互联网 发布:南自通华 知乎 编辑:程序博客网 时间:2024/05/22 06:44

题意

给出一个n的排列,求将该排列划分成两部分,使得其分别为上升序列的方案有多少种。答案模998244353.
n<=100000

分析

一开始yy出了一个dp方程,看形式应该是可以用数据结构来优化的。
实际上有更简单的方法。
首先把无解的情况判掉。若最长下降子序列大于2的无解。
我们可以在两两逆序对之间连边,那么答案就是2^联通块数量。
对于每个连通块,我们将其最大的元素仍到set里面。每加入一个元素,我们就把set里面大于它的连通块合并,再扔到set里面就好了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<set>using namespace std;typedef long long LL;const int N=100005;const int MOD=998244353;int n,a[N],f[N],c[N];set<int> w;void ins(int x,int y){    while (x<=n)    {        c[x]=max(c[x],y);        x+=x&(-x);    }}int find(int x){    int ans=0;    while (x)    {        ans=max(ans,c[x]);        x-=x&(-x);    }    return ans;}int ksm(int x,int y){    int ans=1;    while (y)    {        if (y&1) ans=(LL)ans*x%MOD;        x=(LL)x*x%MOD;y>>=1;    }    return ans;}int main(){    scanf("%d",&n);    for (int i=1;i<=n;i++) scanf("%d",&a[i]);    int mx=0;    for (int i=1;i<=n;i++)    {        f[i]=find(n-a[i])+1;        ins(n-a[i]+1,f[i]);        mx=max(mx,f[i]);    }    if (mx>2)    {        printf("0");        return 0;    }    for (int i=1;i<=n;i++)    {        int mx=a[i];        set<int>::iterator it=w.upper_bound(a[i]);        while (it!=w.end())        {            mx=max(mx,*it);            set<int>::iterator tmp=it;            it++;w.erase(tmp);        }        w.insert(mx);    }    printf("%d",ksm(2,w.size()));    return 0;}
原创粉丝点击