UVA-11640 Mayor Election

题解:dp预处理出每个候选人l[i]~u[i]之间的方案数,可以知道这是个背包问题。与以往不同的是这一次背的物品不能喝上一次的相同。那么,s[j] - f[j][k]这个状态即上一次以任意海报结束 - 以k类海报结束就不会再与当前第k类海报相连接了。

然后就是对每对候选人做ntt了,但是这个模数有点特别,查表可知,P=786433时,G = 10.

#include"cstdio"
#include"iostream"
#include"cstring"
#define debug(x) cout<<#x<<" = "<<x<<" ";
using namespace std;
typedef long long LL;
const int MX = 1e5+5;
const int mod = 786433;

int p[55],li[55],ri[55];
int num[55][15];
LL dp[MX<<2],tp[MX<<2],s[2002],f[2002][10];

const int P = 786433;
const int G = 10;
const int NUM = 20;
LL  wn[NUM];
LL  va[MX],vb[MX];
LL quick_mod(LL a, LL x, LL mod) {
    LL ans = 1;
    a %= mod;
    while(x) {
        if(x & 1)ans = ans * a % mod;
        x >>= 1;
        a = a * a % mod;
    }
    return ans;
}
//在程序的开头就要放
void GetWn() {
    for(int i = 0; i < NUM; i++) {
        int t = 1 << i;
        wn[i] = quick_mod(G, (P - 1) / t, P);
    }
}
void Rader(LL F[], int len) {
    int j = len >> 1;
    for(int i = 1; i < len - 1; i++) {
        if(i < j) swap(F[i], F[j]);
        int k = len >> 1;
        while(j >= k)j -= k,k >>= 1;
        if(j < k) j += k;
    }
}
void NTT(LL F[], int len, int t) {
    Rader(F, len);
    int id = 0;
    for(int h = 2; h <= len; h <<= 1) {
        id++;
        for(int j = 0; j < len; j += h) {
            LL E = 1;
            for(int k = j; k < j + h / 2; k++) {
                LL u = F[k];
                LL v = E * F[k + h / 2] % P;
                F[k] = (u + v) % P;
                F[k + h / 2] = (u - v + P) % P;
                E = E * wn[id] % P;
            }
        }
    }
    if(t == -1) {
        for(int i = 1; i < len / 2; i++)swap(F[i], F[len - i]);
        LL inv = quick_mod(len, P - 2, P);
        for(int i = 0; i < len; i++)F[i] = F[i] * inv % P;
    }
}
void Conv(LL a[], LL b[], int len) {
    NTT(a, len, 1);
    NTT(b, len, 1);
    for(int i = 0; i < len; i++)a[i] = a[i] * b[i] % P;
    NTT(a, len, -1);
}

void upd(LL &a, LL b)
{
    a = (a+b)%mod;
    if(a < 0) a += mod;
}

void solve()
{
    int n, maxr = 0, sumr = 0;
    scanf("%d",&n);
    memset(dp,0,sizeof(dp));
    for(int i = 1; i <= n; i++){
        scanf("%d%d%d",&p[i],&li[i],&ri[i]);
        for(int j = 0; j < p[i]; j++){
            scanf("%d",&num[i][j]);
        }
        sumr += ri[i];
        for(int j = 0; j <= ri[i]; j++){
            s[j] = 0;
            for(int k = 0; k < p[i]; k++)
                f[j][k] = 0;
        }
        //不连续物品背包的转移
        s[0] = 1;
        for(int j = 0; j <= ri[i]; j++){
            for(int k = 0; k < p[i]; k++){
                int t = min(ri[i]-j,num[i][k]);
                for(int l = 1; l <= t; l++){
                    LL tmp = (s[j]-f[j][k]);
                    upd(s[j+l],tmp);
                    upd(f[j+l][k],tmp);
                }
            }
        }

        for(int j = 0; j < li[i]; j++)
            tp[j] = 0;
        for(int j = li[i]; j <= ri[i]; j++)
            tp[j] = s[j];

        int len = 1;
        while((len<<1) <= (sumr<<1)) len <<= 1;
        for(int j = ri[i]+1; j < len; j++)
            tp[j] = 0;

        if(i == 1) for(int j = 0; j < len; j++) dp[j] = tp[j];
        if(i > 1) Conv(dp,tp,len);
    }
    int Q, query = 0;
    for(scanf("%d",&Q); Q; Q--){
        int L;
        scanf("%d",&L);
        printf("Query %d: %lld\n",++query,dp[L]);
    }
    printf("\n");
}
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif // LOCAL
    int T,cas = 0;
    scanf("%d",&T);
    GetWn();
    while(T--){
        printf("Case #%d:\n",++cas);
        solve();
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值