异或运算
Luogu
BZOJ
比较裸。
第一眼以为(n,mle1000)。。。
然后发现(nle1000,mle300000,qle500)。
那么应该是个(O(nqlog m))复杂度的算法了。(log^2m)估计过不了。
因为(m)这一维非常大,所以我们考虑对(y)数组建可持久化01Trie。
然后每次询问,我们把(x)数组中的在询问范围内的拿出来(称之为询问(x)数组)。
从高位往低位贪心,每次在当前节点上遍历所有询问(x)数组,看一下01Trie上跟它异侧的(y)有多少个,然后判断当前位应该是(0)还是(1),然后暴力把询问(x)数组的元素往下跳就行了。
#include<bits/stdc++.h>
#define pi pair<int,int>
#define fi first
#define se second
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('
');}
}
using namespace IO;
const int N=1007,M=300007;
int n,m,a[N],root[M],cnt,sum[M<<5],ch[M<<5][2];pi pos[N];
void insert(int &p,int pre,int x)
{
sum[p=++cnt]=sum[pre]+1;
for(int i=30,t=p,d;~i;--i) d=x>>i&1,ch[t][!d]=ch[pre][!d],pre=ch[pre][d],sum[t=ch[t][d]=++cnt]=sum[pre]+1;
}
void query(int l,int r,int pre,int p,int k)
{
int ans=0;
for(int i=l;i<=r;++i) pos[i]=pi(pre,p);
for(int i=30,j,s,x,f;~i;--i)
{
for(s=0,j=l;j<=r;++j) x=a[j]>>i&1,s+=sum[ch[pos[j].se][!x]]-sum[ch[pos[j].fi][!x]];
if(k<=s) ans|=1<<i,f=1; else k-=s,f=0;
for(j=l;j<=r;++j) x=(a[j]>>i&1)^f,pos[j].fi=ch[pos[j].fi][x],pos[j].se=ch[pos[j].se][x];
}
write(ans);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=m;++i) insert(root[i],root[i-1],read());
for(int Q=read(),u,v,l,r,k;Q;--Q) u=read(),v=read(),l=read(),r=read(),k=read(),query(u,v,root[l-1],root[r],k);
return Flush(),0;
}
解密运算
Luogu
BZOJ
什么叫国际思维题啊。
首先我们知道这个字符串中出现的数就是给出的那(n+1)个数去掉(0)。
然后我们有一个想法:排序后第一个字符串的开头是(0),这样我们就可以确定了(0)和第一个字符串末尾数字的相对关系。。。。这样我们总共可以确定(n+1)对相对关系。
此时如果数字都不重复的话我们就可以直接得到答案了,但是数字是有重复的,我们不知道我们和具体哪一个数字有相对关系。。。
现在我们来解决这个问题。
我们知道题目给我们的顺序就是字典序升序。
所以我们以字符串末尾的数字为第一关键字,题目给的顺序(这个数后面的数开头的字符串的字典序)为第二关键字排序,就能够得到以每个数开头的字符串的字典序。
这个东西和后缀数组里面的那个原理极为相似。
现在我们把所有字符串按字典序排序,并且知道它的第一个数字,又知道以这个数后面一个数开头的字符串的字典序。那么我们直接跳就完事了。
#include<bits/stdc++.h>
using namespace std;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
#define pi pair<int,int>
#define x first
#define y second
pi a[200007];
int main()
{
int i,p,n=read();read();
for(i=0;i<=n;++i) a[i]=pi(read(),i);
sort(a,a+n+1),p=a[0].y;
for(i=1;i<=n;++i) printf("%d ",a[p].x),p=a[p].y;
}
平方运算
Luogu
BZOJ
注意两个OJ输入格式有细微差别。
我们打表/猜测可以发现平方运算在(mod p)意义下有循环节。
打表发现所有循环节的(lcm)不会超过(60)。
因此我们可以开一棵线段树,记录一下每个点的值是否进入循环节。
如果进入循环节就直接用预处理打表的东西来做,一次平方相当于循环移位。
否则暴力修改,打表可以发现最多(11)次就可以进入循环节。
因此复杂度大概是(O(71nlog n))的,反正肯定能过。
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
void write(int x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('
');}
}using namespace IO;
const int N=100007;
int n,m,p,lcm,is[N],vis[N],tag[N<<2],in[N<<2];vector<int>sum[N<<2];
int sqr(int x){return x*x%p;}
void init()
{
lcm=1;
for(int i=0,x,l;i<p;++i)
{
for(x=i,l=0;!vis[x];) vis[x]=1,x=sqr(x),++l;
if(x==i) lcm=lcm*l/__gcd(lcm,l),is[i]=1;
for(x=i;vis[x];) vis[x]=0,x=sqr(x);
}
}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
#define root 1,1,n
void pushup(int p)
{
in[p]=in[ls]&in[rs];
for(int i=0;i<(in[p]? lcm:1);++i) sum[p][i]=sum[ls][i]+sum[rs][i];
}
void modify(int p,int v){tag[p]=(tag[p]+v)%lcm,sum[p].insert(sum[p].end(),sum[p].begin(),sum[p].begin()+v),sum[p].erase(sum[p].begin(),sum[p].begin()+v);}
void pushdown(int p){modify(ls,tag[p]),modify(rs,tag[p]),tag[p]=0;}
void work(int p,int v)
{
sum[p][0]=v,in[p]=is[v];
if(in[p]) for(int i=1;i<lcm;++i) sum[p][i]=sqr(sum[p][i-1]);
}
void build(int p,int l,int r)
{
sum[p].resize(lcm);
if(l==r) return work(p,read());
build(ls,l,mid),build(rs,mid+1,r),pushup(p);
}
void update(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R&&in[p]) return modify(p,1);
if(l==r) return work(p,sqr(sum[p][0]));
if(tag[p]) pushdown(p);
if(L<=mid) update(ls,l,mid,L,R);
if(R>mid) update(rs,mid+1,r,L,R);
pushup(p);
}
int query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return sum[p][0];
if(tag[p]) pushdown(p);
return (L<=mid? query(ls,l,mid,L,R):0)+(R>mid? query(rs,mid+1,r,L,R):0);
}
int main()
{
n=read(),m=read(),p=read(),init(),build(root);
for(int l,r;m;--m)
if(read()==2) l=read(),r=read(),write(query(root,l,r));
else l=read(),r=read(),update(root,l,r);
return Flush(),0;
}