luogu P3747 [六省聯考2017]相逢是問候

背景:

寫一下這一週集訓做的題目吧。
其實是切不動題了。

題目傳送門:

https://www.luogu.org/problem/P3747

題意:

一個序列,兩種操作。
[1].[1].aia_i變爲caic^{a_i}
[2].[2].i=lraimod  p\sum_{i=l}^{r}a_i\mod p

思路:

類似的題目詳見:luogu P3934 Nephren Ruq Insania\text{luogu P3934 Nephren Ruq Insania}
同樣的我們需要解決ccc...aimod  pc^{c^{c^{...^{a_i}}}}\mod p的問題。
我們依然可以考慮歐拉降冪。
那麼同樣的來做就可以了。
對於區間和的求解用線段樹即可。
特別地,如果當前區間的最小修改次數已經到了ϕ(ϕ(ϕ(...)))\phi(\phi(\phi(...)))的修改爲11的次數,直接結束即可,因爲此時的指數已經爲11了。

可是容易算得時間複雜度爲:nlog2logn\log^2\log 值域log3\log^3級別直接爆炸,分數爲90pts\text{90pts}
不妨光速冪即可。
注意,blockblock應取2(p+1)2*(\sqrt{p}+1),而需要指數加的部分用個數組存下來即可。

代碼:

#pragma GCC optimize("Ofast")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
#define I inline
#define R register
using namespace std;
	int n,m,p,c,limit,len=0,block;
	int a[50010],phi[50];
	LL POW1[50][20010],POW2[50][20010];
	bool flag1[50][20010],flag2[50][20010];
	struct node{int l,r,lc,rc,d,cnt,sum;} tr[400010];
I int get_phi(int x)
{
	int t=x,tot=x;
	for(R int i=2;i*i<=t;i++)
		if(!(x%i))
		{
			tot=tot/i*(i-1);
			while(!(x%i)) x/=i;
		}
	return x>1?tot/x*(x-1):tot;
}
void init()
{
	phi[limit=0]=p;
	while(phi[limit]>1) limit++,phi[limit]=get_phi(phi[limit-1]);
	phi[++limit]=1;
	block=2*(sqrt(p)+1);
	for(int i=0;i<=limit;i++)
	{
		POW1[i][0]=1;
		for(int j=1;j<=block;j++)
		{
			POW1[i][j]=POW1[i][j-1]*c;
			if(POW1[i][j]>=phi[i]) POW1[i][j]%=phi[i],flag1[i][j]=true;
			flag1[i][j]|=flag1[i][j-1];
		}
	}
	for(int i=0;i<=limit;i++)
	{
		POW2[i][0]=1;
		for(int j=1;j<=block;j++)
		{
			POW2[i][j]=POW2[i][j-1]*POW1[i][block];
			if(POW2[i][j]>=phi[i]) POW2[i][j]%=phi[i],flag2[i][j]=true;
			flag2[i][j]|=flag2[i][j-1];
		}
	}
}
void build(int l,int r)
{
	int now=++len;
	tr[now]=(node){l,r,-1,-1,0,0,0};
	if(l==r) tr[now].sum=a[l]%p;
	if(l<r)
	{
		int mid=(l+r)>>1;
		tr[now].lc=len+1; build(l,mid);
		tr[now].rc=len+1; build(mid+1,r);
		tr[now].sum=((LL)tr[tr[now].lc].sum+tr[tr[now].rc].sum)%p;
	}
}
bool flag;
int get_POW(int x,int id)
{
	int t1=x%block,t2=x/block;
	LL tot=POW2[id][t2]*POW1[id][t1];
	flag|=flag2[id][t2]|flag1[id][t1];
	if(tot>=phi[id]) tot%=phi[id],flag=true;
	return tot;
}
I int work(int tot,int t)
{
	if(tot>phi[t]) tot=tot%phi[t]+phi[t];
	for(R int i=t;i>=1;i--)
	{
		flag=false;
		tot=get_POW(tot,i-1);
		if(flag) tot+=phi[i-1];
	}
	return tot;
}
void change(int now,int l,int r)
{
	if(tr[now].cnt>=limit) return;
	if(tr[now].l==tr[now].r) {tr[now].sum=work(a[tr[now].l],++tr[now].cnt);return;}
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(r<=mid) change(lc,l,r);
	else if(l>mid) change(rc,l,r);
	else change(lc,l,mid),change(rc,mid+1,r);
	tr[now].cnt=min(tr[lc].cnt,tr[rc].cnt);
	tr[now].sum=((LL)tr[lc].sum+tr[rc].sum)%p;
}
int findsum(int now,int l,int r)
{
	if(tr[now].l==l&&tr[now].r==r) return tr[now].sum%p;
	int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
	if(r<=mid) return findsum(lc,l,r);
	else if(l>mid) return findsum(rc,l,r);
	else return ((LL)findsum(lc,l,mid)+findsum(rc,mid+1,r))%p;
}
I char getc()
{
	static char buf[1<<20],*fs,*ft;
	return(fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin)),fs==ft)?EOF:*fs++;
}
I void read(int &x)
{
	char ch=getc();
	for(x=0;ch=='\n'||ch==' ';ch=getc());
	for(;ch!='\n'&&ch!=' '&&ch!=EOF;ch=getc())
		x=((x+(x<<2))<<1)+(ch^0x30);
}
int main()
{
	int t,x,y;
	read(n),read(m),read(p),read(c);
	init();
	for(R int i=1;i<=n;i++)
		read(a[i]);
	build(1,n);
	for(R int i=1;i<=m;i++)
	{
		read(t),read(x),read(y);
		if(x>y) swap(x,y);
		if(!t) change(1,x,y); else printf("%d\n",findsum(1,x,y));
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章