BZOJ 4881: [Lydsy1705月赛]线段游戏 动态规划 + 线段树

Description

quailty和tangjz正在玩一个关于线段的游戏。在平面上有n条线段,编号依次为1到n。其中第i条线段的两端点坐
标分别为(0,i)和(1,p_i),其中p_1,p_2,...,p_n构成了1到n的一个排列。quailty先手,他可以选择一些互不相交
的线段,将它们拿走,当然他也可以一条线段也不选。然后tangjz必须拿走所有剩下的线段,若有两条线段相交,
那么他就输了,否则他就赢了。注意若quailty拿走了全部线段,那么tangjz也会胜利。quailty深深喜欢着tangjz
,所以他不希望tangjz输掉游戏,请计算他有多少种选择线段的方式,使得tangjz可以赢得游戏。

Input

第一行包含一个正整数n(1<=n<=100000),表示线段的个数。
第二行包含n个正整数p_1,p_2,...,p_n(1<=p_i<=n),含义如题面所述。

Output

输出一行一个整数,即tangjz胜利的方案数,因为答案很大,请对998244353取模输出。

问题可以转化为:
给定一个序列,求将这个序列分成两个上升子序列的方案数.
令 $f_{i,j}$ 表示一个序列选 $i$,令一个序列的最大值为 $p_{j}$ 的方案数 ($j<i$).
考虑转移:

  • $f_{i,j}=f_{i-1,j},j<i-1$ 且 $p_{i-1}<p_{i}$
  • $f_{i,i-1}=\sum f_{i-1,k},p_{k}<p_{i}$

$f_{i,j}$ 的转移只和 $f_{i-1,?}$ 的转移有关,所以可以将第一维滚掉.
而每一次 $f$ 的更新只有两种:区间清零,用一个区间和来更新单点的点值.
这些操作可以用线段树来实现.

#include <cstdio> 
#include <algorithm>  
#define N 100006 
#define mod 998244353
#define setIO(s) freopen(s".in", "r" , stdin) 
using namespace std;    
int a[N], n ; 
struct Node 
{ 
    int sum, lazy ;       
}t[N << 2];   
inline void mark(int now) 
{ 
    t[now].sum = 0, t[now].lazy = 1; 
}
inline void pushdown(int l, int r, int now) 
{
    int mid = (l + r) >> 1;    
    if(t[now].lazy) 
    {
        mark(now << 1); 
        if(r > mid) mark(now << 1 | 1);       
        t[now].lazy = 0; 
    }          
}
inline void pushup(int l, int r, int now) 
{
    int mid = (l + r) >> 1; 
    t[now].sum = t[now << 1].sum ; 
    if(r > mid) t[now].sum += t[now << 1 | 1].sum, t[now].sum %= mod ;     
}
inline void update(int l, int r, int now, int p, int v) 
{
    if(l == r) 
    {
        t[now].sum += v, t[now].sum %= mod;      
        return ; 
    }  
    int mid = (l + r) >> 1; 
    pushdown(l, r, now);            
    if(p <= mid) update(l, mid, now << 1, p, v);    
    else update(mid + 1, r, now << 1 | 1, p, v);        
    pushup(l, r, now);           
}
inline int query(int l, int r, int now, int L, int R) 
{
    if(l >= L && r <= R) return t[now].sum ;  
    pushdown(l, r, now); 
    int mid = (l + r) >> 1, re = 0; 
    if(L <= mid) re += query(l, mid, now << 1, L, R); 
    if(R > mid)  re += query(mid + 1, r, now << 1 | 1, L, R); 
    return re % mod; 
}
int main() 
{ 
    // setIO("input");  
    int i , j ; 
    scanf("%d", &n);  
    for(i = 1 ; i <= n ; ++ i) scanf("%d", &a[i]);      
    update(0, n, 1, 0, 1);     
    for(i = 2 ; i <= n ; ++ i) 
    {
        int sum = query(0, n , 1, 0, a[i] - 1);            
        if(a[i] < a[i - 1]) mark(1);          
        update(0, n, 1, a[i - 1], sum);                    
    } 
    printf("%d\n", query(0, n, 1, 0, n) * 2 % mod);          
    return 0; 
}

  

posted @ 2019-08-15 15:44  EM-LGH  阅读(167)  评论(0编辑  收藏  举报