版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38786088/article/details/81504423
题意:对数组进行两个操作:
1、C L R 修改区间[L,R]上每个值为本身的平方,取mod 2018
2、Q L R 查询区间[L,R]的数值和
说说感受吧! 这道题,我暴力打表,在2018内的数分为两种:一种自幂的所有数中初始段不会出现第两次,后面会形成环;另外一种是初始就在循环内;另外我猜想了所有循环节的最小公倍数应该是6;可是后面的操作我却并没有理清,就是一些细节的地方没有想清,很明显是线段树的题目,也肯定是要利用到这个循环节,我也想了如果一个区间内有数字不包含在循环段里,我是不能用延迟标记的,我需要暴力修改,最多6次,最多几次就进入了循环节;但是关键段区间都是在循环节内,我怎么整段区间修改为下一循环节,这里我没有理清楚,没办法,去牛客讨论区取经!看了别人的题解线段树节点记录的和有6个!我豁然开朗,区间保存循环和出现的6中结果,就可以利用延迟标记了!真的是思维上太呆了!
floyd判环算法(龟兔赛跑算法) ,可以辅助我们跑出所有的值自幂不超过6次后会进入循环过节,循环节的长度为1、2、3、6,最小公倍数为6.所以整段区间进入循环后,我们将仅需要将自幂n次变为一次赋值。
贴上:牛客网题解
下面附上个人写的龟兔赛跑算法的代码:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#define llt long long
using namespace std;
/******************************
*** floyd 判环算法(龟兔赛跑算法)
******************************/
const int mod = 2018;
int nxt[3000];
int floyd_circle(int s,int &pos,int &num){//初始点 pos 用来保存进入循环的位置 num记录s到pos的长度
int pos_rab = s;
int pos_tor = s;
int cnt = 0; num = 0;
do{
//兔1s跑两步
for(int i=1;i<=2;++i)
if(nxt[pos_rab]==-1){pos=pos_rab;num++;return -1;}//无环,终点为pos
else pos_rab = nxt[pos_rab];
//乌龟1s跑1步
pos_tor = nxt[pos_tor];
}while(pos_rab!=pos_tor);
//相遇了
int k = pos_rab;//记录相遇位置
pos_tor = s;
while(pos_rab!=pos_tor){
pos_rab = nxt[pos_rab];
pos_tor = nxt[pos_tor];
cnt++;
}
num = cnt; cnt = 0; pos = pos_rab;
/*求循环节的长度*/
do{
cnt++;
pos_rab = nxt[pos_rab];
}while(pos_rab!=pos);
return cnt;
}
int main(){
//建边
for(int i=0;i<mod;++i)
nxt[i] = i*i%mod;
for(int i=0;i<mod;++i){
int pos,num;
int k = floyd_circle(i,pos,num);
if(k!=-1){
printf("%d %d %d %d\n",i,pos,num,k);
`
}else printf("%d %d %d\n",i,pos,num);
}
}
接下来就是线段树的事了!我手写代码错了两个地方,调了一个晚上,代码能力太差·
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <set>
#define llt long long
#define lson (rt<<1)
#define rson ((rt<<1)+1)
using namespace std;
const int N = 50000 + 777;
const int mod = 2018;
bool circle[mod];
bool check(int x){
int pre = x;
for(int i=0;i<6;++i)
x = x*x%mod;
return x==pre;
}
struct stnode{
int l,r,idx;
bool in_circle;
llt sum[6];
}p[N<<2];
llt A[N];
char s[3];
void update_top(int rt){
if(p[lson].in_circle&&p[rson].in_circle){
p[rt].in_circle = true;
for(int i=0;i<6;++i)
p[rt].sum[i] = p[lson].sum[i] + p[rson].sum[i];
}else{
p[rt].in_circle = false;
for(int i=0;i<1;++i)
p[rt].sum[i] = p[lson].sum[i] + p[rson].sum[i];
}
}
void update(int rt,int val){
int tmp[8];
for(int i=0;i<6;++i)
tmp[i] = p[rt].sum[(i+val)%6];
for(int i=0;i<6;++i)
p[rt].sum[i] = tmp[i];
p[rt].idx +=val;//延迟标记写val,不要写成1
p[rt].idx %=6;
}
void update_down(int rt){
int val = p[rt].idx;
if(val==0) return ;
//将延迟回馈给左儿子
update(lson,val);
//将延迟回馈给右儿子
update(rson,val);
p[rt].idx = 0;
}
void mkStree(int rt,int l,int r){
p[rt].l = l;
p[rt].r = r;
p[rt].idx = 0;
p[rt].in_circle = false;
memset(p[rt].sum,0,sizeof(p[rt].sum));
if(l==r){
p[rt].sum[0] = A[l];
if(circle[A[l]]){
p[rt].in_circle = true;
for(int i=1;i<6;++i)
p[rt].sum[i] = p[rt].sum[i-1]*p[rt].sum[i-1]%mod;
}else p[rt].in_circle = false;
return;
}
int mid = (l+r)>>1;
mkStree(lson,l,mid);
mkStree(rson,mid+1,r);
update_top(rt);
}
void modify(int rt,int l,int r){
if(l<=p[rt].l&&p[rt].r<=r&&p[rt].in_circle){//必须在环内
update(rt,1);
return ;
}
if(p[rt].l == p[rt].r){//没有在环内
p[rt].sum[0] = p[rt].sum[0]*p[rt].sum[0]%mod;
if(circle[p[rt].sum[0]]){//如果经过这次幂后,成功进环
p[rt].in_circle = true;
for(int i=1;i<6;++i)
p[rt].sum[i] = p[rt].sum[i-1]*p[rt].sum[i-1]%mod;
}
return ;
}
update_down(rt);
int mid = (p[rt].l+p[rt].r)>>1;
if(l<=mid) modify(lson,l,r);// 不要写成modify(lson,l,mid);
if(mid<r) modify(rson,l,r);
update_top(rt);
}
llt query(int rt,int l,int r){
if(l<=p[rt].l&&p[rt].r<=r) return p[rt].sum[0];
update_down(rt);
int mid = (p[rt].l+p[rt].r)>>1;
llt ans = 0;
if(l<=mid) ans += query(lson,l,r);
if(mid<r) ans += query(rson,l,r);
return ans;
}
int main(){
for(int i=0;i<mod;++i) circle[i] = check(i);
int cas;
scanf("%d",&cas);
for(int i=1;i<=cas;++i){
int n,m;
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lld",&A[i]);
mkStree(1,1,n);
scanf("%d",&m);
printf("Case #%d:\n",i);
while(m--){
int l,r;
scanf("%s%d%d",s,&l,&r);
if(s[0]=='C') modify(1,l,r);
else printf("%lld\n",query(1,l,r));
}
}
return 0;
}