「清华集训2017」某位歌姬的故事

「清华集训2017」某位歌姬的故事

[我永远喜欢 IA].png

解题思路

首先对于覆盖一个位置的所有限制,只需要取最小的那个即可,然后所有限制的位置都不交了,分别计算方案数相乘即可。

那么问题就转化为若干个位置可以放黑白两种颜色的球,要求每一个限制区间里面必须有一个黑球,求方案数。其中一个白球有 \(m_i-1\) 种方案。

假设对第 \(k\) 种限制计算,令 \(dp(i,j)\) 为前 \(i\) 个位置最后一个黑球放在 \(j\) 的方案数,维护一下每一个位置对应的限制最左边的黑球可以放在哪里,记为 \(lim[i]\)
\[ dp(i,j)=dp(i-1,j)\times (m_k-1)^{size_i} \ (lim[i]\leq j < i)\\ dp(i,i)=\sum dp(i-1,j)\times(m_k^{sizei}-(m_k-1)^{size_i}) \]
复杂度 \(\mathcal O(Tn^2)\)

code
/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define int ll
const int N = 2005, mod = 998244353;
inline int Pow(int a, int b){
    int ans = 1;
    for(; b; b >>= 1, a = a * a % mod)
        if(b & 1) ans = ans * a % mod;
    return ans;
}
set<int> st;
int n, q, A, l[N], r[N], w[N], s[N], t[N], dp[N][N], mn[N], mx[N], len, total_data;
inline int calc(int now){
    int tot = 0;
    for(int i = 1; i <= len; i++) if(mn[i] == now) t[++tot] = i;
    if(!tot) return -1;
    for(int i = 1; i <= tot; i++) mx[i] = 0;
    for(int i = 1; i <= q; i++) if(w[i] == now){
        int L = lower_bound(t + 1, t + tot + 1, l[i]) - t;
        int R = lower_bound(t + 1, t + tot + 1, r[i]) - t - 1;
        mx[R] = max(mx[R], L);
    }
    dp[0][0] = 1;
    for(int i = 1; i <= tot; i++){
        dp[i][i] = 0;
        int choose0 = Pow(now - 1, s[t[i]+1] - s[t[i]]);
        int choose1 = Pow(now, s[t[i]+1] - s[t[i]]);
        for(int j = 0; j < i; j++){
            if(j >= mx[i]) dp[i][j] = dp[i-1][j] * choose0 % mod; else dp[i][j] = 0;
            dp[i][i] = (dp[i][i] + dp[i-1][j] * ((choose1 + mod - choose0) % mod) % mod) % mod;
        }
    }
    int res = 0;
    for(int i = 0; i <= tot; i++) res = (res + dp[tot][i]) % mod;
    return res;
}
inline void solve(){
    st.clear(), read(n), read(q), read(A), s[len=1] = 1;
    for(int i = 1; i <= q; i++){
        read(l[i]), read(r[i]), read(w[i]), r[i]++;
        s[++len] = l[i], s[++len] = r[i], st.insert(w[i]);
    }
    s[++len] = n + 1, sort(s + 1, s + len + 1);
    len = unique(s + 1, s + len + 1) - s - 1;
    for(int i = 1; i <= len; i++) mn[i] = A + 1;
    for(int i = 1; i <= q; i++){
        l[i] = lower_bound(s + 1, s + len + 1, l[i]) - s;
        r[i] = lower_bound(s + 1, s + len + 1, r[i]) - s;
        for(int j = l[i]; j < r[i]; j++) mn[j] = min(mn[j], w[i]);
    }
    int res = 1, x;
    static set<int>::iterator it;
    for(it = st.begin(); it != st.end(); it++){
        if(~(x = calc(*it))) res = res * x % mod; 
        else return (void) (puts("0"));
    }
    for(int i = 1; i < len; i++) 
        if(mn[i] == A + 1) res = res * Pow(A, s[i+1] - s[i]) % mod;
    printf("%lld\n", res);
}
signed main(){
    int T; read(T); while(T--) solve();
    return 0;
}   

转载于:https://www.cnblogs.com/mangoyang/p/10500638.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值