P6533 RELATIVNOST 题解
P6533 RELATIVNOST 题解
题目
洛谷 P6533 RELATIVNOST
分析
题目中要求至少有
我们注意到
又因为对于每组数据有
思路
对于线段树上的每一个节点
这一部分可能有点绕,如果不清楚的话建议感性理解结合代码看一看(在 update 函数里面)。
对于有
而对于线段树的每个叶子节点,即单独一个人,他购买了彩色画的方案数就是他最多会买的彩色画的数量(即
对于每一个人
更详细的实现步骤详见代码注释。
代码
#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张及以上的彩色画的方案数
}
}
更新
2023/12/21感谢 @linyuhuai 的提醒,修正了一个错误。