题目大意
一个长度为n的序列,初始全0。有两种操作。
1、将[l,r]重新设置A mod B,2A mod B,3A mod B,4A mod B,5A mod B……(r-l+1)A mod B。
2、求[l,r]的和。
前置技能
我们如何求
∑ni=1iA mod B
?
就是求
∑ni=1iA−⌊iAB⌋∗B
A∗n∗(n+1)2−B∗∑ni=1⌊iAB⌋
考虑怎么求后面那个东西,设为solve(A,B,n)
∑ni=1⌊iAB⌋
如果A是B的倍数,我们很容易计算,设A=Bk,那么答案为
k∗n∗(n+1)2
,可以直接计算出来。
否则,我们考虑A=Bk+a
∑ni=1⌊i(Bk+a)B⌋
k∗n∗(n+1)2+∑ni=1⌊iaB⌋
然后A用a代替。
这其实就是把A模了B,使得
A<B
。
∑ni=1⌊iAB⌋
∑ni=1∑⌊nAB⌋j=1[iAB>=j]
∑⌊nAB⌋j=1∑ni=1[iAB>=j]
∑⌊nAB⌋j=1(n−∑ni=1[iAB<j])
∑⌊nAB⌋j=1(n−∑ni=1[jBA>i])
∑⌊nAB⌋j=1(n−∑ni=1⌊jBA⌋−[A|jB])
注意到[A|jB]等价于[B|iA]
即设L=jB,那么B|L,现在假设A|L
因为jB<=nA,所以L<=nA,那么L可以用iA表示,又有B|L,所以等价。
⌊nAB⌋∗n∗(n+1)2+∑ni=1[B|iA]−∑⌊nAB⌋j=1⌊jBA⌋
第二项可以计算A与B的lcm来得到最小满足条件的i,然后i的倍数一定都合法,否则一定不合法,贡献为
⌊ni⌋
最后一项就是
solve(B,A,⌊jBA⌋)
,递归计算即可
观察solve的前两项,每次就是从(A,B)变成了(B,A%B),所以这是一个类欧几里得算法,复杂度为log。
线段树
有了这个技能可以直接线段树维护标记。
标记形式就是A和B系数,以及从几开始。
只要实现了solve可以前缀和得到。
我不知道如何处理爆掉的情况……贴个除了爆了类型肯定正确的代码。。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxtot=1000000+10;
int left[maxtot],right[maxtot],a[maxtot],b[maxtot],low[maxtot],up[maxtot];
ll sum[maxtot];
int i,j,k,l,r,t,n,m,tot,root;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
ll lcm(int a,int b){
return (ll)a*b/gcd(a,b);
}
ll solve(int A,int B,ll n){
ll i,k,l=0;
if (A%B==0){
k=A/B;
l=(ll)k*n*(n+1)/2;
return l;
}
k=A/B;
l=(ll)k*n*(n+1)/2;
A%=B;
l+=(ll)n*((ll)n*A/B);
i=lcm(A,B)/A;
l+=(ll)(n/i);
l-=solve(B,A,(ll)n*A/B);
return l;
}
ll work(int A,int B,int n){
return (ll)A*n*(n+1)/2-(ll)B*solve(A,B,n);
}
void mark(int &x,int A,int B,int L,int R){
if (!x) x=++tot;
sum[x]=work(A,B,R)-work(A,B,L-1);
a[x]=A;b[x]=B;low[x]=L;up[x]=R;
}
void down(int x,int l,int r){
int mid=(l+r)/2;
if (b[x]){
mark(left[x],a[x],b[x],low[x],low[x]+mid-l);
mark(right[x],a[x],b[x],low[x]+mid-l+1,up[x]);
b[x]=0;
}
}
void change(int &x,int l,int r,int a,int b,int A,int B,int L,int R){
if (!x) x=++tot;
if (l==a&&r==b){
mark(x,A,B,L,R);
return;
}
down(x,l,r);
int mid=(l+r)/2;
if (b<=mid) change(left[x],l,mid,a,b,A,B,L,R);
else if (a>mid) change(right[x],mid+1,r,a,b,A,B,L,R);
else{
change(left[x],l,mid,a,mid,A,B,L,L+mid-a);
change(right[x],mid+1,r,mid+1,b,A,B,L+mid-a+1,R);
}
sum[x]=sum[left[x]]+sum[right[x]];
}
ll query(int x,int l,int r,int a,int b){
if (!x) return 0;
if (l==a&&r==b) return sum[x];
down(x,l,r);
int mid=(l+r)/2;
if (b<=mid) return query(left[x],l,mid,a,b);
else if (a>mid) return query(right[x],mid+1,r,a,b);
else return query(left[x],l,mid,a,mid)+query(right[x],mid+1,r,mid+1,b);
}
int main(){
n=read();m=read();
while (m--){
t=read();
if (t==1){
l=read();r=read();j=read();k=read();
change(root,1,n,l,r,j,k,1,r-l+1);
}
else{
l=read();r=read();
printf("%lld\n",query(root,1,n,l,r));
}
}
}
平衡树做法
待填