【JZOJ4941】宝石魔术 题解

题目大意

  有 Q 种操作。
  1、加入一个魔力为 x 的宝石;
  2、删去一个魔力为 x 的宝石(保证操作合法);
  3、询问有多少种选取宝石的方法,使得选取的魔力和为 x;(不同下标的宝石视为不同,即两种方法不同当且仅当两种方法选取的宝石有不同)
  4、询问有多少种选取宝石的方法,使得选取的魔力和为 x。(不同魔力的宝石视为不同,即两种方法不同当且仅当某一种魔力值的宝石数量不同)

  Q, x<=10^4,时限 3s。

解法1

  这种东西肯定先想到 dp。
  设 f[i] 表示操作3条件下,魔力和为 i 的方案数;设 g[i] 表示操作4的方案数。f 是个01背包,而 g 是个多重背包。

  然后这就是个动态dp了。
  f 的维护比较简单,插入就倒着扫一遍,删除就正着扫一遍。
  g 的维护比较麻烦,主要是它的转移跟数量有关。实现程序时瞎搞一下也可以使其成为线性。
  一次dp是 O(x) 的,所以总时间是 O(Qx)。

解法2

  考虑dp的生成函数。贴题解了。
  以下的 $cnt[i]$ 表示魔力值为 i 的宝石数量,$f[i][j]$ 表示01背包的dp

代码

解法1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include<cstdio>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long LL;

const int maxn=1e4+5;
const LL mo=1e9+7;

int n,cnt[maxn];
LL f[maxn],g[maxn];

int ReadInt()
{
char ch=getchar();
int data=0;
while (ch<'0' || ch>'9') ch=getchar();
do{
data=data*10+ch-'0';
ch=getchar();
} while (ch>='0' && ch<='9');
return data;
}
char ReadChar()
{
char ch=getchar();
while (ch!='i' && ch!='q' && ch!='d' && ch!='R' && ch!='T') ch=getchar();
return ch;
}

LL gj[maxn],gp[maxn];
void NewG(int x,int z)
{
if (cnt[x])
{
fo(i,0,x-1) gj[i]=g[i];
fo(i,x,n)
{
g[i]=(g[i]-gj[i-x]+mo)%mo;
gj[i]=(gj[i-x]+g[i])%mo;
if (i-cnt[x]*x>=0) gj[i]=(gj[i]-g[i-cnt[x]*x]+mo)%mo;
}
}
cnt[x]+=z;
if (cnt[x])
{
memcpy(gp,g,sizeof(g));
fo(i,0,x-1) gj[i]=gp[i];
fo(i,x,n)
{
g[i]=(g[i]+gj[i-x])%mo;
gj[i]=(gj[i-x]+gp[i])%mo;
if (i-cnt[x]*x>=0) gj[i]=(gj[i]-gp[i-cnt[x]*x]+mo)%mo;
}
}
}

int Q;
int main()
{
n=10000;
f[0]=g[0]=1;

scanf("%d",&Q);
while (Q--)
{
char ty=ReadChar();
if (ty=='i')
{
int x=ReadInt();

fd(i,n,x) f[i]=(f[i]+f[i-x])%mo;
NewG(x,1);
} else if (ty=='d')
{
int x=ReadInt();

fo(i,x,n) f[i]=(f[i]-f[i-x]+mo)%mo;
NewG(x,-1);
} else
{
char now=ReadChar();
int x=ReadInt();

printf("%lld\n",(now=='T') ?f[x] :g[x] );
}
}
}