[类欧几里得算法 线段树] BZOJ 1938 [CROATIAN2010] ALADIN

直接在线段树上区间覆盖咯
怎么求和?

x=lr(Ax) mod B=x=lrAxBx=lrAxB

后半部分直接用类欧求就好了 类似 [类欧几里得算法 数论] BZOJ 2987 Earthquake 但是更简单

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline ll gcd(ll a,ll b ) { return b?gcd(b,a%b):a; }
inline ll calc(ll a,ll b,ll n){
  ll x=a/b,sum=x*n*(n+1)/2; a%=b;
  if (!a || !b) return sum;
  return sum+n*(a*n/b)-calc(b,a,a*n/b)+n/b;
}
inline ll solve(ll a,ll b,ll n){ 
  if (n<1) return 0;
  ll g=gcd(a,b); 
  return n*(n+1)/2*a-b*calc(a/g,b/g,n);
}

const int N=200005;
const int M=N<<1;

struct info{
  int l,r,a,b;
  info(int l=0,int r=0,int a=-1,int b=0):l(l),r(r),a(a),b(b) { }
  ll val(){
    return solve(a,b,r)-solve(a,b,l-1);
  }
};

int ls[M],rs[M]; ll sum[M]; info F[M];
int ncnt;

int sx[N],icnt;
#define L(x) (sx[x])
#define R(x) (sx[x+1]-1)

inline void mark(int &x,info f){
  if (!x) x=++ncnt;
  F[x]=f; sum[x]=f.val();
}

inline void modify(int &x,int l,int r,int a,int b,int ql,int qr){
  if (!x) x=++ncnt;
  int mid=(l+r)>>1;
  if (ql<=l && r<=qr){
    mark(x,info(L(l)-L(ql)+1,R(r)-L(ql)+1,a,b));
    return;
  }
  if (F[x].a!=-1){
    mark(ls[x],info(F[x].l,F[x].l+R(mid)-L(l),F[x].a,F[x].b));
    mark(rs[x],info(F[x].l+L(mid+1)-L(l),F[x].r,F[x].a,F[x].b));
    F[x].a=-1;
  }
  if (ql<=mid) modify(ls[x],l,mid,a,b,ql,qr);
  if (qr>mid) modify(rs[x],mid+1,r,a,b,ql,qr);
  sum[x]=sum[ls[x]]+sum[rs[x]];
}

inline ll query(int &x,int l,int r,int ql,int qr){
  if (!x) return 0;
  int mid=(l+r)>>1;
  if (ql<=l && r<=qr)
    return sum[x];
  if (F[x].a!=-1){
    mark(ls[x],info(F[x].l,F[x].l+R(mid)-L(l),F[x].a,F[x].b));
    mark(rs[x],info(F[x].l+L(mid+1)-L(l),F[x].r,F[x].a,F[x].b));
    F[x].a=-1;
  }
  ll ret=0;
  if (ql<=mid) ret+=query(ls[x],l,mid,ql,qr);
  if (qr>mid) ret+=query(rs[x],mid+1,r,ql,qr);
  return ret;
}

const int maxn=1000000000;
int n,rt;


struct event{
  int t,l,r,a,b;
}eve[N];

int main(){
  int Q; int order,l,r,a,b;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(Q); sx[++icnt]=n+1;
  for (int i=1;i<=Q;i++){
    read(eve[i].t); read(eve[i].l); read(eve[i].r);
    sx[++icnt]=eve[i].l; sx[++icnt]=eve[i].r+1;
    if (eve[i].t==1)
      read(eve[i].a),read(eve[i].b),eve[i].a%=eve[i].b;
  }
  sort(sx+1,sx+icnt+1);
  icnt=unique(sx+1,sx+icnt+1)-sx-1;
  for (int i=1;i<=Q;i++){
    l=lower_bound(sx+1,sx+icnt+1,eve[i].l)-sx;
    r=upper_bound(sx+1,sx+icnt+1,eve[i].r)-sx-1;
    if (eve[i].t==1)
      modify(rt,1,icnt,eve[i].a,eve[i].b,l,r);
    else
      printf("%lld\n",query(rt,1,icnt,l,r));
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值