NHOI 2016 T6 方案数

简化题意

n 个人拿红蓝两色球。每一个人只能拿一种颜色的球。

对于第 i 个人,最多只能拿 ai 个红球,bi 个蓝球,但不可以不拿。

接下来有 q 个操作,每一次选择某个人(输入给定),改变两个值。具体为输入 p,A,B ,表示将 apA,bpB,注意的是,修改一直生效。

至少C 个人拿了红球的总方案数为多少?在每一次操作完后输出,但开头不必输出。(答案 mod 10007

范围

n,q105C20

ai,bi109

分析

Step1:

考虑动态规划。

设状态 fi,j 表示前 i 个人中,有 j 个人选择了红球的方案数。

接下来分析怎样转移得到。

  1. 假设第 i 个人拿的是红球,那么就有 ai 种情况(因为这个人能拿 1ai 个红球),那么这种状态就是从 fi1,j1 转移过来。

  2. 假设第 i 个人拿的是蓝球,那么就有 bi 种情况,那么这种状态就是从 fi1,j 转移过来。

根据方案数的加法原理和乘法原理,得到转移为:

fi,j=fi1,j1×ai+fi1,j×bi

所以我们需要求的答案就是:

i=Cnfn,i

空间复杂度为 O(n2),时间复杂度为 O(qn2)

Step2:

我们发现,C20 这个关键条件一直没有被用到。

尝试使用求补集的方法,即用总方案-不合法方案。

对于第 i 个人,既能拿红球,也能拿蓝球,所以第 i 个人就有 ai+bi 种情况。所以总方案为:

i=1n(ai+bi)

那么不合法的方案自然是拿红球的人数 <C 了,所以不合法方案为:

i=0C1fn,i

空间复杂度为 O(nC),时间复杂度为 O(qnC)。如果考虑本题的时限为 10s ,应该可以通过此题。

(upd:发现这个复杂度高达 1010,应该还是过不了)

Step3:

考虑使用线段树维护。

思路和动态规划一样,但是那么既然需要修改,那么我们也需要一些数据结构。

对于每一个叶子节点 t 都是一个结构体。

struct data{ll f[25];};//f[i]表示该节点所代表的区间拿了i个红球的方案数

维护的 up 函数,我们也是通过加法原理和乘法原理、和两个子节点的信息来维护。具体就是这样:

for(int i=20;i>=0;i--)
{
	t[k].f[i]=0;
	for(int j=i;j>=0;j--) 
		t[k].f[i]=(t[k].f[i]+t[ls].f[j]*t[rs].f[i-j])%mod;
}

同时,这颗线段树还需要维护总方案,即

i=1n(ai+bi)

搞定这些,你就能 AC 这题了。

放一下代码吧(67行):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
const ll mod=10007;
int n,C,q;
ll a[N],b[N];
struct data{ll f[25];};//f[i]表示该节点所代表的区间拿了i个红球的方案数 
struct Seg1
{
	data t[N<<2];ll mul[N<<2];
	void up(int k)
	{
		int ls=k<<1,rs=ls|1;
		for(int i=20;i>=0;i--)
		{
			t[k].f[i]=0;
			for(int j=i;j>=0;j--) 
				t[k].f[i]=(t[k].f[i]+t[ls].f[j]*t[rs].f[i-j])%mod;
		}
		mul[k]=(mul[ls]*mul[rs])%mod;
	}
	void build(int k,int l,int r)
	{
		if(l==r)
		{
			t[k].f[1]=a[l]%mod;
			t[k].f[0]=b[l]%mod;
			mul[k]=(a[l]+b[l])%mod;
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);build(k<<1|1,mid+1,r);
		up(k);
	}
	void upd(int k,int l,int r,int u)
	{
		if(l==r)
		{
			t[k].f[1]=a[l]%mod;
			t[k].f[0]=b[l]%mod;
			mul[k]=(a[l]+b[l])%mod;
			return;
		}
		int mid=(l+r)>>1;
		if(u<=mid)upd(k<<1,l,mid,u);
		else upd(k<<1|1,mid+1,r,u);
		up(k);
	}
}T;
int main(){
	scanf("%d%d",&n,&C);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
	T.build(1,1,n);
	scanf("%d",&q);
	while(q--)
	{
		int p;
		scanf("%d",&p);scanf("%lld%lld",&a[p],&b[p]);
		T.upd(1,1,n,p);
		ll ans=0;
		for(int i=0;i<C;i++)ans=(ans+T.t[1].f[i])%mod;
		printf("%lld\n",((T.mul[1]-ans)%mod+mod)%mod);
	}
	return 0;
}

最终时间复杂度为 O(qlognC2),空间复杂度为 O(nC)

posted @   大眼仔Happy  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示