链接:https://www.nowcoder.com/acm/contest/163/H
来源:牛客网
题目描述
You have N integers A1, A2, ... , AN. You are asked to write a program to receive and execute two kinds of instructions:
1. C a b means performing Ai = (Ai2 mod 2018) for all Ai such that a ≤ i ≤ b.
2. Q a b means query the sum of Aa, Aa+1, ..., Ab. Note that the sum is not taken modulo 2018.
输入描述:
The first line of the input is T(1≤ T ≤ 20), which stands for the number of test cases you need to solve.
The first line of each test case contains N (1 ≤ N ≤ 50000).The second line contains N numbers, the initial values of A1, A2, ..., An. 0 ≤ Ai < 2018. The third line contains the number of operations Q (0 ≤ Q ≤ 50000). The following Q lines represents an operation having the format "C a b" or "Q a b", which has been described above. 1 ≤ a ≤ b ≤ N.
输出描述:
For each test case, print a line "Case #t:" (without quotes, t means the index of the test case) at the beginning.
You need to answer all Q commands in order. One answer in a line.
示例1
输入
复制
1
8
17 239 17 239 50 234 478 43
10
Q 2 6
C 2 7
C 3 4
Q 4 7
C 5 8
Q 6 7
C 1 8
Q 2 5
Q 3 4
Q 1 8
输出
复制
Case #1:
779
2507
952
6749
3486
9937
这个题真的好强啊。一开始啥也不会,后来请教了线段树大神舍友慢慢学会了一点点。
对于平方取模操作,在2018 以内的每一个数,一定可以通过若干步进入循环节(对于这个题好像是最多4步,如果打表没错的话), 并且也可以暴力的求出循环节最长为6 ,那么这样这个题就找到了突破口。我们可以从循环节入手。 在每个节点中记录该节点的循环节,以及该节点的sum在循环节的哪个位置,这样每次操作一次右移一次,对于一个父节点,只有他的两个子节点全部进入循环节,那么这个节点才可以进入循环节。 而且这里有个小操作,大神的方法是可以预先将每个叶节点向前走> 4 步 这样记录的这个叶节点的val 就一定是进入循环节的。 代码中有一点点解释。
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
//
#define lson (i<<1)
#define rson (i<<1|1)
using namespace std;
const int N =5e4+5;
int a[N];
int pre[N][15];
struct node
{
int l,r,sum;
int pos,lz;
int cnt,flag;
int val[7];
}tr[N<<2];
void init()
{
for(int i=0;i<2018;i++){
int tmp=i;
for(int j=0;j<15;j++){
pre[i][j]=tmp;
tmp=(tmp*tmp)%2018;
}
}
}
void push_up(int i)
{
tr[i].sum=tr[lson].sum+tr[rson].sum;
if(tr[lson].flag&&tr[rson].flag){ // 如果两个子节点都进入循环节,那么标志着这个父节点也可以进入循环节
// 循环节中的数其实是两个子节点的加和。
tr[i].pos=0;
for(int j=0;j<6;j++){
tr[i].val[j]=tr[lson].val[(tr[lson].pos+j)%6]+tr[rson].val[(tr[rson].pos+j)%6];
}
tr[i].flag=1;
}
}
void push_down(int i)
{
if(tr[i].lz)
{
int xx=tr[i].lz;
tr[lson].lz+=xx; tr[rson].lz+=xx;
tr[lson].lz%=6; tr[rson].lz%=6;
tr[lson].pos=(tr[lson].pos+xx)%6; tr[rson].pos=(tr[rson].pos+xx)%6;
tr[lson].sum=tr[lson].val[tr[lson].pos]; tr[rson].sum=tr[rson].val[tr[rson].pos];
tr[i].lz=0;
}
}
void build(int i,int l,int r)
{
tr[i].l=l; tr[i].r=r; tr[i].sum=0; tr[i].lz=0; tr[i].pos=0; tr[i].flag=0; tr[i].cnt=0;
for(int j=0;j<6;j++) tr[i].val[j]=0;
if(l==r){
tr[i].sum=a[l];
for(int j=0;j<6;j++){
tr[i].val[j]=pre[a[l]][5+j];
}
return ;
}
int mid=(tr[i].l+tr[i].r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
push_up(i);
}
void update(int i,int l,int r)
{
if(tr[i].l==l&&tr[i].r==r){
if(tr[i].flag){
tr[i].pos=(tr[i].pos+1)%6;
tr[i].lz=(tr[i].lz+1)%6;
tr[i].sum=tr[i].val[tr[i].pos];
return ;
}
if(l==r){ // 表示如果是根节点 并且还没有进入循环
tr[i].cnt++;
tr[i].sum=(tr[i].sum*tr[i].sum)%2018;
if(tr[i].cnt==5) tr[i].flag=1;
return ;
}
// 如果当前节点不是根节点并且还没有进入循环节,那么就要向下继续更新.
}
push_down(i);
int mid=(tr[i].l+tr[i].r)>>1;
if(r<=mid) update(lson,l,r);
else if(l>mid) update(rson,l,r);
else{
update(lson,l,mid); update(rson,mid+1,r);
}
push_up(i);
}
int query(int i,int l,int r)
{
if(tr[i].l==l&&tr[i].r==r){
return tr[i].sum;
}
push_down(i);
int mid=(tr[i].l+tr[i].r)>>1;
if(r<=mid) return query(lson,l,r);
else if(l>mid) return query(rson,l,r);
else{
return query(lson,l,mid)+query(rson,mid+1,r);
}
}
char op[5];
int n,m;
int main()
{
int l,r;
init();
int T;
scanf("%d",&T);
int kk=0;
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build(1,1,n);
scanf("%d",&m);
printf("Case #%d:\n",++kk);
while(m--)
{
scanf("%s %d %d",op,&l,&r);
if(op[0]=='C'){
update(1,l,r);
}
else{
int ans=query(1,l,r);
printf("%d\n",ans);
}
}
}
return 0;
}
另附一份打表的代码:
#include<bits/stdc++.h>
using namespace std;
int ans[10005];
int cc[10005];
int vis[2020];
int main()
{
int tot=0;
int mincnt=1000005;
for(int num=0;num<2018;num++)
{
int cnt=0;
int tmp=num;
int f=0;
while(1)
{
cnt++;
tmp=tmp*tmp%2018;
//cout<<"cnt "<<cnt<<" tmp "<<tmp<<endl;
if(tmp==num) break;
if(cnt>105){
f=1;
break;
}
}
if(f==0){
ans[++tot]=num;
cc[tot]=cnt;
}
}
cout<<"mincnt "<<mincnt<<endl;
for(int i=1;i<=tot;i++){
cout<<" num "<<ans[i]<<" cc "<<cc[i]<<endl;
}
for(int i=1;i<=tot;i++){
vis[ans[i] ]=1;
}
int maxx=-1;
for(int num=0;num<2018;num++)
{
int cnt=0;
int tmp=num;
int f=0;
while(1)
{
if(vis[tmp]) break;
cnt++;
tmp=tmp*tmp%2018;
}
maxx=max(maxx,cnt);
}
cout<<"maxx "<<maxx<<endl;
return 0;
}