题解: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;
}