hdu 3074 Multiply game

        一个数列,有两种操作。一是修改数列中某个数,二是求数列中连续一段的积。

        很明显的线段树单点更新模版题。

#include<stdio.h>
#include<iostream> 
using namespace std; 
  
#define lch(x) ((x)<<1) 
#define rch(x) (((x)<<1)|1) 
#define ll long long 
const int mod = 1000000007; 

int a[50010]; 
  
struct node{ 
    int l,r; 
    ll val; 
}tree[200010]; 
  
void push_up(int nd){ 
    if(tree[nd].l==tree[nd].r){ 
        return ; 
    }
    tree[nd].val=tree[lch(nd)].val*tree[rch(nd)].val; 
    tree[nd].val%=mod; 
}

void build_tree(int nd,int l,int r){ 
    tree[nd].l=l;  tree[nd].r=r; 
    if(l==r){ 
        tree[nd].val=a[l]; 
        return; 
    } 
    int mid=(l+r)>>1; 
    build_tree(lch(nd),l,mid); 
    build_tree(rch(nd),mid+1,r); 
    push_up(nd); 
} 

ll query(int nd,int l,int r){ 
    if(tree[nd].l==l&&tree[nd].r==r){ 
        return tree[nd].val; 
    } 
    int mid= (tree[nd].l+tree[nd].r)>>1; 
    if(r<=mid){ 
        return query(lch(nd),l,r); 
    }else{ 
        if(l>mid){ 
            return query(rch(nd),l,r); 
        }else{ 
            ll a=query(lch(nd),l,mid); 
            ll b=query(rch(nd),mid+1,r); 
            return (a*b)%mod; 
        } 
    } 
} 

void update(int nd,int pos,int val){ 
    if(tree[nd].l==tree[nd].r){ 
        tree[nd].val=val; 
        return; 
    } 
    int mid=(tree[nd].l+tree[nd].r)>>1; 
    if(pos<=mid){ 
        update(lch(nd),pos,val); 
    }else{ 
        update(rch(nd),pos,val); 
    } 
    push_up(nd); 
} 

int main(){ 
    int t; 
    cin>>t; 
    while(t--){ 
        int n; 
        cin>>n; 
        for(int i=1;i<=n;i++){ 
            scanf("%d",&a[i]); 
        } 
        build_tree(1,1,n); 
        int q; 
        cin>>q; 
        for(int i=1;i<=q;i++){ 
            int op,a,b; 
            scanf("%d%d%d",&op,&a,&b); 
            if(op){ 
                update(1,a,b); 
            }else{		//query 
                ll ans=query(1,a,b); 
                printf("%I64d\n",ans); 
            } 
        } 
    } 
    return 0; 
} 



        天真的我曾经以为BIT只能维护和,不能维护积。但是比赛时一个学弟用BIT+逆元很好地教育了我。。膜拜一下Orz。。自己实现调了很久才调出来。

        题目求的是数列一个区间的积,输出取模后的结果。当某个数改变后,我们要在BIT里先除以旧的数再乘上新的数。但是由于取模的原因,不能用除法,所以必须用乘上旧的数的逆元来代替。

#include <stdio.h>
#include <iostream>
using namespace std;

#define ll long long

const int maxn=50010;
const int mod=1e9+7;
int T,n;

int a[maxn];
ll c[maxn];

//扩展欧几里德 
void ExEuclid(int a,int b,ll &x,ll &y,ll &q){
	if(b==0){
		x=1;y=0;q=a;
		return;
	}
	ExEuclid(b,a%b,y,x,q);
	y-=x*(a/b);
}

//逆元 
int inv(ll num){
	ll x,y,q;
	ExEuclid(num,mod,x,y,q);
	if(q==1)return (x+mod)%mod;
}

int lowbit(int x){
	return x&(-x);
}

void update(int pos,ll val){
	int tmp=val;
	val*=inv(a[pos]);
	val%=mod;
	a[pos]=tmp;
	while(pos<=n){
		c[pos]*=val;
		c[pos]%=mod;
		pos+=lowbit(pos);
	}
}

int product(int pos){
	ll re=1;
	while(pos){
		re*=c[pos];
		re%=mod;
		pos-=lowbit(pos);
	}
	return re;
}

int main(){
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++)a[i]=c[i]=1;
		
		for(int i=1;i<=n;i++){
			int num;
			scanf("%d",&num);
			update(i,num);
		}
		
		int q;
		cin>>q;
		for(int i=1;i<=q;i++){
			int op;
			scanf("%d",&op);
			if(op){
				int k,p;
				scanf("%d%d",&k,&p);
				update(k,p);
			}else{
				int k1,k2;
				scanf("%d%d",&k1,&k2);
				ll part1=product(k1-1);
				ll part2=product(k2);
				ll ans=inv(part1)*part2;
				ans%=mod;
				printf("%I64d\n",ans);
			}
		}
	}
	return 0;
}


你可能感兴趣的:(hdu 3074 Multiply game)