洛谷P6533 [COCI2015-2016#1] RELATIVNOST

题目描述

您是一位计数大师,有一天您的朋友 Luka 出了一道问题来刁难您。

Luka 是一位勤劳的画家,他的画很好,所以会有 𝑛n 个人来买他的画。

画分两种,黑白画与彩色画。

Luka 十分勤劳,所以他有无穷多的画。

Luka 讨厌出售黑白画,所以他希望至少有 𝑐c 个人会买走一张彩色画。

第 𝑖i 个人会至多购买 𝑎𝑖ai​ 张彩色画,𝑏𝑖bi​ 张黑白画,且它们会至少购买一幅画。

但是,客户们只能单独购买彩色画或黑白画。

客户们会不断改变 𝑎𝑖ai​ 与 𝑏𝑖bi​,这种改变会持续 𝑞q 次。

客户以 1∼𝑛1∼n 编号。

您需要求出在每次改变之后,Luka 会有几种方案满足所有需求。

为了防止输出太大,Luka 只需要您告诉他方案数  mod  104+7mod 104+7 的值。

输入格式

第一行为两个整数 𝑛,𝑐n,c。

第二行为 𝑛n 个整数 𝑎𝑖ai​。

第三行为 𝑛n 个整数 𝑏𝑖bi​。

第四行为一个整数 𝑞q。

接下来 𝑞q 行,一行三个整数 𝑝𝑖,𝑎𝑝𝑖,𝑏𝑝𝑖pi​,api​​,bpi​​,第 𝑖i 行表示标号 𝑝𝑖pi​ 的顾客将 𝑎𝑖ai​ 和 𝑏𝑖bi​ 更换成 𝑎𝑝𝑖api​​ 和 𝑏𝑝𝑖bpi​​。

输出格式

共 𝑞q 行,一行一个整数,第 𝑖i 行的值表示进行了第 𝑖i 次改变后,满足条件的方案数  mod  104+7mod 104+7 的值。

输入输出样例

输入 #1

2 2
1 1
1 1
1
1 1 1 

输出 #1

1

输入 #2

2 2
1 2
2 3
2
1 2 2
2 2 2

输出 #2

4
4

输入 #3

4 2
1 2 3 4
1 2 3 4
1
4 1 1

输出 #3

66

说明/提示

样例 1 说明

第一次改变后,我们只有唯一的一种方案,就是向两位用户都出售一张彩色画。

数据范围及限制
  • 对于 30%30% 的数据,保证 𝑛,𝑞≤103n,q≤103。
  • 对于 100%100% 的数据,保证 1≤𝑛,𝑞≤1051≤n,q≤105,1≤𝑐≤201≤c≤20,1≤𝑎𝑖,𝑏𝑖,𝑎𝑝𝑖,𝑏𝑝𝑖≤1091≤ai​,bi​,api​​,bpi​​≤109,1≤𝑝𝑖≤𝑛1≤pi​≤n。
  • Code:

    #pragma gcc optimize(2)//不开O2会T最后一个点
    #include<iostream>
    using namespace std;
    const int MAXN=100005,MOD=10007;
    int a[MAXN],b[MAXN],c,n;//对应题目中的a,b,c,n
    struct Node
    {
    	int l,r,dp[21];
    }tree[MAXN*4];//因为这道题的空间限制只有32MB,有些OJ可能过不了,但是洛谷上这么写还是过了
    inline void update(int id)//更新一个节点的dp数组
    {
    	for(register int i=0;i<=c;i++)
    	{
    		tree[id].dp[i]=0;//每次更新一个节点时先将其清零
    	}
    	for(register int i=0;i<=c;i++)
    	{
    		for(register int j=0;j<=c;j++)
    		{
    			tree[id].dp[min(i+j,c)]=((tree[id*2].dp[i]*tree[id*2+1].dp[j])%MOD+tree[id].dp[min(i+j,c)])%MOD;
                //这行代码比较长,其实去掉取模就是tree[id].dp[min(i+j,c)]+=tree[id*2].dp[i]*tree[id*2+1].dp[j];
                //为啥要用+=?举个例子,左子树两个右子树两个人买彩色画跟左子树一个右子树三个人买彩色画
                //都会计算在有四个人购买彩色画的方案数内。
    		}
    	}
    }
    void build(int l,int r,int id)//建树操作
    {
    	tree[id].l=l,tree[id].r=r;
    	if(l==r)//如果是叶子结点
    	{
    		tree[id].dp[0]=b[l],tree[id].dp[1]=a[l];//那么按照上文所说的修改它的dp[0]和dp[1]
    		return;
    	}
    	build(l,(l+r)/2,id*2);
    	build((l+r)/2+1,r,id*2+1);
    	update(id);//当该节点的左儿子和右儿子建立完成之后更新该节点的dp值
    }
    void change(int id,int A,int B,int fxid)//单点修改操作
    {//id是当前节点的下标,fxid是要修改的节点的下标
    	if(tree[id].l==tree[id].r)
    	{
    		tree[id].dp[0]=B,tree[id].dp[1]=A;
    		return;
    	}
    	if(fxid<=((tree[id].r+tree[id].l)/2))
    		change(id*2,A,B,fxid);
    	if(fxid> ((tree[id].r+tree[id].l)/2))
    		change(id*2+1,A,B,fxid);
    	update(id);//跟上面建树操作一个道理,左右儿子修改完之后更新
    }
    int main()
    {
    	std::ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	cin>>n>>c;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		a[i]%=MOD;//读入之后先取模
    	}
    	for(int i=1;i<=n;i++)
    	{
    		cin>>b[i];
    		b[i]%=MOD;
    	}
    	build(1,n,1);//建树
    	int q,A,B,changeid;
    	cin>>q;
    	while(q--)
    	{
    		cin>>changeid>>A>>B;
    		A%=MOD,B%=MOD;
    		change(1,A,B,changeid);
    		cout<<tree[1].dp[c]<<endl;
            //tree[1].dp[c]就是所有人至少买了c张及以上的彩色画的方案数
    	}
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值