zoukankan      html  css  js  c++  java
  • BZOJ4881 线段游戏(二分图+树状数组/动态规划+线段树)

      相当于将线段划分成两个集合使集合内线段不相交,并且可以发现线段相交等价于逆序对。也即要将原序列划分成两个单增序列。由dilworth定理,如果存在长度>=3的单减子序列,无解,可以先判掉。

      这个时候有两种显然的暴力。

      将点集划分成两部分使内部无边显然就是二分图,于是第一种暴力是在逆序对之间连边,答案即为2连通块个数,因为每个连通块都可以交换黑白点。问题在于暴力连边是n2的,而显然实际有用的边其实只有O(n)条。考虑这样一种连边方式:每个点向后缀最小值、前缀第一个比他大的点连边。瞎归纳归纳就可以证明连这些边就够了。这个前缀第一个比他大的随便找都行,比如弄个bit。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define N 100010
    #define P 998244353
    char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
    int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    int n,a[N],pre[N],suf[N],fa[N],tree[N],cnt;
    inline void chkmax(int &x,int y){if (a[y]>a[x]) x=y;}
    inline void chkmin(int &x,int y){if (a[y]<a[x]) x=y;}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void ins(int k,int x){while (k<=n) tree[k]=min(tree[k],x),k+=k&-k;}
    int query(int k){int s=n;while (k) s=min(s,tree[k]),k-=k&-k;return s;}
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("bzoj4881.in","r",stdin);
        freopen("bzoj4881.out","w",stdout);
        const char LL[]="%I64d
    ";
    #else
        const char LL[]="%lld
    ";
    #endif
        n=read();
        for (int i=1;i<=n;i++) fa[i]=i,a[i]=read(),tree[i]=n+1;a[0]=0,a[n+1]=n+1;
        pre[0]=0;for (int i=1;i<=n;i++) chkmax(pre[i]=pre[i-1],i);
        suf[n+1]=n+1;for (int i=n;i>=1;i--) chkmin(suf[i]=suf[i+1],i);
        for (int i=1;i<=n;i++)
        if (pre[i]!=i&&suf[i]!=i) {cout<<0;return 0;}
        else
        {
            if (suf[i]!=i) fa[find(i)]=find(suf[i]);
            if (pre[i]!=i) fa[find(i)]=find(query(n+1-a[i]));
            ins(n+1-a[i],i);
        }
        for (int i=1;i<=n;i++) if (find(i)==i) cnt++;
        int ans=1;while (cnt--) ans=(ans<<1)%P;
        cout<<ans;
        return 0;
    }

      另一种暴力是一个显然的dp,即设f[i][j]为dp到第i位时,不包含i的集合的最大值是第j个数的方案数。则有f[i][i-1]=Σf[i-1][j] (a[i]>a[j],j<i-1),f[i][j]=f[i-1][j] (a[i]>a[i-1],j<i-1)。将dp数组看成一维的,显然就可以用线段树优化了,即开一棵以a[]为下标的线段树,对f[i][i-1]在线段树上查询前缀更新,如果a[i]<a[i-1]就给整个线段树清零。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    #define N 100010
    #define P 998244353
    char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
    int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    int n,a[N],L[N<<2],R[N<<2],tree[N<<2],lazy[N<<2];
    void update(int k){tree[k]=0,lazy[k]=1;}
    void down(int k){update(k<<1),update(k<<1|1),lazy[k]=0;}
    void up(int k){tree[k]=(tree[k<<1]+tree[k<<1|1])%P;}
    void build(int k,int l,int r)
    {
        L[k]=l,R[k]=r;
        if (l==r) return;
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
    void add(int k,int p,int x)
    {
        if (L[k]==R[k]) {tree[k]+=x;return;} 
        if (lazy[k]) down(k);
        int mid=L[k]+R[k]>>1;
        if (p<=mid) add(k<<1,p,x);
        else add(k<<1|1,p,x);
        up(k);
    }
    int query(int k,int l,int r) 
    {
        if (L[k]==l&&R[k]==r) return tree[k];
        if (lazy[k]) down(k);
        int mid=L[k]+R[k]>>1;
        if (r<=mid) return query(k<<1,l,r);
        else if (l>mid) return query(k<<1|1,l,r);
        else return (query(k<<1,l,mid)+query(k<<1|1,mid+1,r))%P;
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("bzoj4881.in","r",stdin);
        freopen("bzoj4881.out","w",stdout);
        const char LL[]="%I64d
    ";
    #else
        const char LL[]="%lld
    ";
    #endif
        n=read();
        for (int i=1;i<=n;i++) a[i]=read();
        build(1,0,n);add(1,0,2);
        for (int i=2;i<=n;i++)
        {
            int x=query(1,0,a[i]);
            if (a[i]<a[i-1]) update(1);
            add(1,a[i-1],x);
        }
        cout<<tree[1];
        return 0;
    }
  • 相关阅读:
    React在componentDidMount里面发送请求
    React 术语词汇表
    React里受控与非受控组件
    React和Vue等框架什么时候操作DOM
    【LeetCode】79. Word Search
    【LeetCode】91. Decode Ways
    【LeetCode】80. Remove Duplicates from Sorted Array II (2 solutions)
    【LeetCode】1. Two Sum
    【LeetCode】141. Linked List Cycle (2 solutions)
    【LeetCode】120. Triangle (3 solutions)
  • 原文地址:https://www.cnblogs.com/Gloid/p/10025835.html
Copyright © 2011-2022 走看看