201912CSPT5魔数

题意:有一个从 1 1 1 n n n的连续序列,有 q q q次查询,对区间操作 [ l , r ] [l,r] [l,r]
1. 输出 s = f ( A l ) + f ( A l + 1 ) + . . . + f ( A r ) , f ( x ) = ( x 1.输出s=f(A_l)+f(A_{l+1})+...+f(A_r),f(x)=(x 1.输出s=f(Al)+f(Al+1)+...+f(Ar),f(x)=(x% 2009731336725594113 ) 2009731336725594113) 2009731336725594113)% 2019 2019 2019
2. t = s 2.t=s%5 2.t=s
3. A l , A l + 1 , A r 3.A_l,A_{l+1},A_r 3.Al,Al+1,Ar乘上 U t U_t Ut
U [ 0 − 4 ] = 314882150829468584 , 427197303358170108 , 1022292690726729920 , 1698479428772363217 , 2006101093849356424 U[0-4]=314882150829468584,427197303358170108,1022292690726729920,1698479428772363217,2006101093849356424 U[04]=314882150829468584,427197303358170108,1022292690726729920,1698479428772363217,2006101093849356424

#include<bits/stdc++.h>
typedef unsigned long long ull;
using namespace std;
ull U[5]= 
{
    314882150829468584,
    427197303358170108,
    1022292690726729920,
    1698479428772363217,
    2006101093849356424
};
ull mod=2009731336725594113;
unordered_map<ull, int> mp;//mp[乘数]=编号 
ull f[35],a[1000010][35];//f[编号]=乘数,a[i][j]保存的是序列A乘以j后再经过f(x)运算后的结果 
int g[35][35];//转移方程,g[i][j]=k表示序号为i,j的两个数相乘结果会转移成序号为k的数 
int n,q;
ull mul(ull a, ull b)
{
    ull res=0;
    for(;b;b>>=1)
	{
        if(b&1)res=(res+a)%mod;
        a=(a+a)%mod;
    }
    return res;
}
void getConvert()
{
    queue<ull> q;
    int id=1;
    for(int i=0;i<5;i++){q.push(U[i]);mp[U[i]]=id++;}
    while(q.size())
	{
        ull x=q.front();q.pop();
        f[mp[x]]=x;
        for(int i=0;i<5;i++)
		{
            ull t=mul(x,U[i]);
            if(mp[t])continue;   
            mp[t]=id++;q.push(t);
        }
    }
//	for(int i=1;i<=50;i++)cout<<f[i]<<endl;
    for(int i=1;i<=32;i++)
	for(int j=i;j<=32;j++)
	g[i][j]=g[j][i]=mp[mul(f[i],f[j])];
}
int temp[1001];
struct Node
{
	int l,r;
	int sum=0;//区间和
	int tag;//标记当前区间被乘了哪一个数
	int s[33];//保存的是该区间乘以f[i]之后的结果
	//我们大可以把s[]看做是res的一个预测值,因为当前区间只会被乘32个数中的某一个,
	//res也就只会转变为这32个s[i]的某一个
	void trans(int now)
	{
		for(int i=1;i<=32;i++)
		temp[i]=s[g[i][now]];
		for(int i=1;i<=32;i++)
		s[i]=temp[i];
		sum=s[28];//f[28]=1,所以s[28]就是A序列自身 
		//cout<<sum<<" "<<now<<" "<<tag<<endl;
		if(tag==0)tag=now;
		else tag=g[tag][now];
	}
}t[4000010];
void build(ull p,ull l,ull r)
{
	t[p].l=l;t[p].r=r;
	if(l==r)
	{
		for(int i=1;i<=32;i++)
		t[p].s[i]=a[l][i]%2019;
		t[p].sum=t[p].s[28];t[p].tag=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	for(int i=1;i<=32;i++)
    t[p].s[i]=t[p*2].s[i]+t[p*2+1].s[i];
    t[p].sum=t[p].s[28];
	t[p].tag=0;
}
void spread(int p)
{
	if(t[p].tag)
	{
		t[p*2].trans(t[p].tag);
		t[p*2+1].trans(t[p].tag);
		t[p].tag=0;
	}
}
ull ask(ull p,ull l,ull r)
{
	if(t[p].r<l||t[p].l>r)return 0;
	if(t[p].l>=l&&t[p].r<=r)
	return t[p].sum;	
	spread(p);	ull val=0;
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)val+=ask(p*2,l,r);
	if(mid<r)val+=ask(p*2+1,l,r);
	return val;
}
void Mul(ull p,ull l,ull r,ull k)
{
	if(t[p].r<l||t[p].l>r)return ;
	if(t[p].l>=l&&t[p].r<=r)
	{
		t[p].trans(k);
		return ;
	}
	spread(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid)Mul(p*2,l,r,k);
	if(mid<r)Mul(p*2+1,l,r,k);
	for(int i=1;i<=32;i++)t[p].s[i]=t[p*2].s[i]+t[p*2+1].s[i];
    t[p].sum=t[p*2].sum+t[p*2+1].sum;
}
int main()
{
	getConvert();
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=32;j++)
	{
		if(i==1)a[i][j]=f[j]%mod;//因为序列是连续的,所以可以使用加法递推 
		else a[i][j]=(f[j]+a[i-1][j])%mod;
	//	cout<<a[i][j]<<" ";
	}
	build(1,1,n);
	for(int i=1;i<=q;i++)
	{
		ull L,R;
		cin>>L>>R;
		ull ans=ask(1,L,R);
		cout<<ans<<endl;
		Mul(1,L,R,(ans%5)+1);//注意+1 
	}
	return 0;
}
  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值