题目大意
有 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
#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] );
}
}
}