题目质量很赞。这么多道数据结构,却不全是很裸的板子,并且带有卡常题
A:模拟
WA了好几发,贼蠢的题,非负整数是个坑

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 2000050 5 using namespace std; 6 7 int T,n; 8 char str[N1]; 9 10 int main() 11 { 12 scanf("%d",&T); 13 while(T--) 14 { 15 scanf("%d",&n); 16 if(!n){ puts("0"); continue; } 17 scanf("%s",str+1); 18 int cnt=0,tot=0; 19 for(int i=1;i<=n;i++) 20 { 21 if(str[i]=='('){ 22 cnt++; 23 }else{ 24 if(cnt>0) cnt--, tot+=2; 25 } 26 } 27 printf("%d ",n-tot); 28 } 29 return 0; 30 }
B:图论
a[i]向i连边,由于每个点入度都是1,发现图中每个连通块都是基环树,并且只能是环向外有出边。所以连通块个数就是答案

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 1000050 5 #define M1 N1*2 6 using namespace std; 7 8 struct Edge{ 9 int to[M1],nxt[M1],head[N1],cte; 10 void ae(int u,int v) 11 {cte++; to[cte]=v; nxt[cte]=head[u]; head[u]=cte; } 12 }e; 13 int T,n; 14 int a[N1],used[N1]; 15 void dfs(int u) 16 { 17 used[u]=1; 18 for(int j=e.head[u];j;j=e.nxt[j]) 19 { 20 int v=e.to[j]; if(used[v]) continue; 21 dfs(v); 22 } 23 } 24 25 int main() 26 { 27 scanf("%d",&n); 28 int ans=0; 29 for(int i=1;i<=n;i++) 30 { 31 scanf("%d",&a[i]); 32 if(i!=a[i]) e.ae(a[i],i), e.ae(i,a[i]); 33 } 34 for(int i=1;i<=n;i++) 35 { 36 if(!used[i]) dfs(i), ans++; 37 } 38 printf("%d ",ans); 39 return 0; 40 }
C:树状数组
设s[i]是前缀和。考虑暴力做法:枚举i,j(i>j>=0),统计满足s[i]-s[j]<=k(i-j)的i,j对数就行了。正解就挪一下式子,把i,j移到两侧,得到s[i]-ki<=s[j]-kj。所以预处理出所有s[i]-ki,离散一下用树状数组维护就行。不想离散就动态开点线段树2333。统计出来以后gcd一下就行。注意离散化的时候从第2位开始赋离散后的值

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 200010 5 #define M1 N1*2 6 #define ll long long 7 using namespace std; 8 9 ll gcd(ll a,ll b) 10 { 11 if(!b) return a; 12 return gcd(b,a%b); 13 } 14 int n,m; ll K; 15 int a[N1],pos[N1]; 16 ll sa[N1]; 17 18 struct Bit{ 19 ll sum[N1]; 20 void upd(int x,int w){ 21 for(int i=x;i<=m;i+=i&(-i)) sum[i]+=w; 22 } 23 ll query(int x) 24 { 25 ll ans=0; 26 for(int i=x;i>0;i-=i&(-i)) ans+=sum[i]; 27 return ans; 28 } 29 }s; 30 struct node{ 31 int id; ll w; 32 }sp[N1]; 33 int cmp(node a1,node a2) 34 { 35 if(a1.w!=a2.w) return a1.w<a2.w; 36 return a1.id<a2.id; 37 } 38 39 int main() 40 { 41 freopen("a.txt","r",stdin); 42 scanf("%d%lld",&n,&K); 43 for(int i=1;i<=n;i++) scanf("%d",&a[i]), sa[i]=sa[i-1]+a[i]; 44 for(int i=1;i<=n;i++) sp[i].w=sa[i]-K*i, sp[i].id=i; 45 sp[n+1]=(node){0,0}; 46 sort(sp+1,sp+n+2,cmp); 47 m=1, pos[sp[1].id]=m; 48 for(int i=2;i<=n+1;i++) 49 { 50 if(sp[i-1].w!=sp[i].w) m++; 51 pos[sp[i].id]=m; 52 } 53 s.upd(pos[n],1); ll ans=0; 54 for(int i=n-1;i>=0;i--) 55 { 56 ans+=s.query(pos[i]); 57 s.upd(pos[i],1); 58 } 59 // printf("%lld ",ans); 60 ll t1=ans,t2=1ll*(n+1)*n/2, g=gcd(t1,t2); 61 printf("%lld/%lld ",t1/g,t2/g); 62 return 0; 63 }
D:线段树
很普通的线段树题,我还卡了将近40min,菜爆了
线段树维护一下?记录f[rt][0]表示左侧余下的)数量,f[rt][1]表示右侧余下(的数量,这两个变量用于判断括号序列是否合法,它们向上合并的式子并不难。再考虑最大嵌套怎么求,考虑暴力做法,打差分,把(当成+1,)当成-1,求一次前缀和,因为保证括号序列合法,求完以后序列元素的最大值就是最大嵌套层数。把这个做法扔线段树里就可以动态了!当前区间的答案就是max(左区间答案,左区间前缀和+右区间答案)。
左侧多余的)和右侧多余的(不必特殊讨论,因为它们迟早得被匹配上,假设左右都给补全就行。

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 1000005 5 #define M1 N1*4 6 #define ll long long 7 using namespace std; 8 9 struct SEG{ 10 int f[M1][2],sum[M1],ma[M1]; 11 #define ls rt<<1 12 #define rs rt<<1|1 13 void pushup(int rt) 14 { 15 f[rt][0]=f[ls][0]+max(f[rs][0]-f[ls][1],0); //left remain ) -1 16 f[rt][1]=f[rs][1]+max(f[ls][1]-f[rs][0],0); //right remain ( 1 17 sum[rt]=sum[ls]+sum[rs]; 18 ma[rt]=max(ma[ls],sum[ls]+ma[rs]); 19 } 20 void upd(int x,int l,int r,int rt,int w) 21 { 22 if(l==r){ 23 if(w==-1) f[rt][0]=1, f[rt][1]=0; // ) 24 if(w==0) f[rt][0]=0, f[rt][1]=0; // * 25 if(w==1) f[rt][0]=0, f[rt][1]=1; // ( 26 sum[rt]=w, ma[rt]=w; return; 27 } 28 int mid=(l+r)>>1; 29 if(x<=mid) upd(x,l,mid,ls,w); 30 else upd(x,mid+1,r,rs,w); 31 pushup(rt); 32 } 33 #undef ls 34 #undef rs 35 }s; 36 37 int check() 38 { 39 if(s.sum[1] || s.f[1][0] || s.f[1][1]) return -1; 40 return s.ma[1]; 41 } 42 43 int n,ma,mi,m; 44 char comd[N1],str[N1]; 45 46 int main() 47 { 48 freopen("a.txt","r",stdin); 49 scanf("%d",&n); 50 if(!n) return 0; 51 scanf("%s",comd+1); 52 int tmp=0,pos,ans; 53 ma=0,mi=0; 54 for(int i=1;i<=n;i++) 55 if(comd[i]=='R') tmp++, ma=max(ma,tmp); 56 else if(comd[i]=='L') tmp--, mi=min(mi,tmp); 57 pos=1-mi; m=ma-mi+1; 58 for(int i=1;i<=n;i++) 59 { 60 if(comd[i]=='L') pos--; 61 else if(comd[i]=='R') pos++; 62 else if(comd[i]=='(') s.upd(pos,1,m,1,1); 63 else if(comd[i]==')') s.upd(pos,1,m,1,-1); 64 else s.upd(pos,1,m,1,0); 65 ans=check(); 66 printf("%d ",ans); 67 } 68 puts(""); 69 return 0; 70 }
E:二维RMQ维护gcd
板子题,看到n=500以为$O(n^{3}+n^{2}log^{2}n)$能卡过,结果一直T,咋卡常也没用,数据很强。会寝室路上突然想到RMQ可以拓展到二维,枚举每个点再二分答案就行了,可以优化成$O(n^{2}logn+n^{2}log^{2}n)$。回去写了一发,终于过了

1 #include <ctime> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define N1 505 6 #define ll long long 7 using namespace std; 8 9 inline int gcd(int x,int y) 10 { 11 if(!y) return x; 12 return gcd(y,x%y); 13 } 14 template <typename _T> void read(_T &ret) 15 { 16 ret=0; _T fh=1; char c=getchar(); 17 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 18 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 19 ret=ret*fh; 20 } 21 22 int n,m,ma; 23 int a[N1][N1],lg[N1]; 24 int g[N1][N1][12]; 25 int qg(int x,int y,int len) 26 { 27 return gcd( gcd( g[x][y][lg[len]],g[x][y+len-1-(1<<lg[len])+1][lg[len]]) , gcd(g[x+len-1-(1<<lg[len])+1][y][lg[len]], g[x+len-1-(1<<lg[len])+1][y+len-1-(1<<lg[len])+1][lg[len]]) ) ; 28 } 29 30 int main() 31 { 32 scanf("%d%d",&n,&m); 33 clock_t st,ed; 34 st=clock(); 35 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) read(a[i][j]); 36 ma=max(n,m); lg[1]=0; 37 int ans=0,num=0; 38 for(int i=2;i<=ma;i++) lg[i]=lg[i>>1]+1; 39 for(int i=1;i<=n;i++) 40 { 41 for(int j=1;j<=m;j++) g[i][j][0]=a[i][j]; 42 } 43 for(int k=1;k<=lg[ma];k++) 44 for(int i=1;i+(1<<k)-1<=n;i++) 45 { 46 for(int j=1;j+(1<<k)-1<=m;j++) 47 { 48 g[i][j][k]=gcd( gcd(g[i][j][k-1],g[i][j+(1<<(k-1))][k-1]) , gcd(g[i+(1<<(k-1))][j][k-1],g[i+(1<<(k-1))][j+(1<<(k-1))][k-1]) ); 49 } 50 } 51 int tmp,l,r,mid,k; 52 for(int i=1;i<=n;i++) 53 for(int j=1;j<=m;j++) 54 { 55 k=0, l=1, r=min(min(m-j,j-1),min(n-i,i-1)); 56 while(l<=r) 57 { 58 mid=(l+r)>>1; tmp=qg(i-mid,j-mid,2*mid+1); 59 if(tmp%a[i][j]==0) k=mid, l=mid+1; 60 else r=mid-1; 61 } 62 if((2*k+1)*(2*k+1)>ans) ans=(2*k+1)*(2*k+1), num=1; 63 else if((2*k+1)*(2*k+1)==ans) num++; 64 } 65 printf("%d %d ",ans,num); 66 return 0; 67 }
F:贪心+set 好题
考虑一条链的情况,搞个set记录现在有哪些佣兵可用,再用田忌赛马策略。对于每个任务,找后继(第一个比需要的能力值大的忍者),如果没有后继,就把最次的佣兵安排上。
环怎么办。。。卡了1h多想不出弃疗看题解去了
看了byaidu学长的题解,原来可以断环??!
如果佣兵和任务是一一映射,那么答案唯一,特判一下就好
对于其他的情况,会有的任务有很多佣兵,那么它之后的几个任务的佣兵都可以由它提供剩余的佣兵,它们之间可以互相影响。那么可能相互影响的任务一定是环上连续的一段。如果这一段的任务数量>1,那么最后一个肯定是空任务结尾,且它和前面的任务都不能影响后面的任务。整个序列都是一段的情况依然成立
有了上面的结论,把环给转一下,就可以化成链,舍去尾对头的影响了!
用链表实现任务的添加和删除,用set找后继
另外vector是真不快

1 #include <set> 2 #include <ctime> 3 #include <vector> 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #define N1 500005 8 #define ll long long 9 #define its set<int>::iterator 10 #define itv vector<int>::iterator 11 using namespace std; 12 13 template <typename _T> void read(_T &ret) 14 { 15 ret=0; _T fh=1; char c=getchar(); 16 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 17 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 18 ret=ret*fh; 19 } 20 int n; 21 int id[N1],a[N1],b[N1],num[N1]; 22 struct node{ 23 int l,r; 24 }lst[N1]; 25 struct Edge{ 26 int to[N1],nxt[N1],head[N1],cte; 27 void ae(int u,int v) 28 { cte++; to[cte]=v, nxt[cte]=head[u], head[u]=cte; } 29 }e; 30 void pre_lst() 31 { 32 lst[1].l=n, lst[1].r=2; 33 for(int i=2;i<n;i++) lst[i].l=i-1,lst[i].r=i+1; 34 lst[n].l=n-1, lst[n].r=1; 35 } 36 int del(int i) 37 { 38 lst[lst[i].l].r=lst[i].r; 39 lst[lst[i].r].l=lst[i].l; 40 return lst[i].r; 41 } 42 int find_st() 43 { 44 pre_lst(); 45 int tot=n,i=1,res,st; 46 while(tot) 47 { 48 if(!num[i]){ i=lst[i].r; continue; } 49 res=num[i]; 50 while(res) 51 { 52 i=del(i); res--; tot--; 53 if(res==0) break; 54 res+=num[i]; 55 } 56 } 57 if(!tot) st=(i==n?1:i+1); else st=i; 58 return st; 59 } 60 set<int>s; 61 vector<int>mer[N1]; 62 int solve(int i) 63 { 64 pre_lst(); 65 int tot=n,j,ans=0;its k; 66 for(;tot;) 67 { 68 // if(!num[i]){ i=lst[i].r; continue; } 69 for(j=0;j<mer[i].size();j++) s.insert(b[mer[i][j]]); 70 // for(j=e.head[i];j;j=e.nxt[j]) s.insert(b[e.to[j]]); 71 if(s.empty()){ i=lst[i].r; continue; } 72 k=s.upper_bound(a[i]); 73 if(k==s.end()){ 74 k=s.begin(); s.erase(*k); 75 }else{ 76 s.erase(*k); ans++; 77 } 78 i=del(i), tot--; 79 } 80 return ans; 81 } 82 83 int main() 84 { 85 // freopen("f.txt","r",stdin); 86 // freopen("a.in","r",stdin); 87 // freopen("f.out","w",stdout); 88 clock_t sta,ed,tmp; 89 sta=clock(); 90 scanf("%d",&n); 91 for(int i=1;i<=n;i++) read(id[i]), num[id[i]]++; 92 for(int i=1;i<=n;i++) mer[id[i]].emplace_back(i); 93 // for(int i=1;i<=n;i++) read(id[i]), num[id[i]]++, e.ae(id[i],i); 94 for(int i=1;i<=n;i++) read(a[i]); //任务 95 for(int i=1;i<=n;i++) read(b[i]); //佣兵 96 int flag=1; int ans=0; 97 for(int i=1;i<=n;i++) if(num[i]!=1){ flag=0; break; } 98 if(flag) 99 { 100 for(int i=1;i<=n;i++) 101 { 102 if(b[i]>a[id[i]]) ans++; 103 } 104 printf("%d ",ans); 105 return 0; 106 } 107 int st=find_st(); 108 ans=solve(st); 109 printf("%d ",ans); 110 // ed=clock(); printf("%.7lf s ",(double)(ed-sta)/CLOCKS_PER_SEC); 111 return 0; 112 }
G:线段树合并+离散化
线段树合并板子题,但此题很毒瘤似乎卡空间,于是乎需要离散,把dis[x]和dis[x]+L一起离散2333
线段树合并又又又又又又又又又又tnnd打错了,调了30minWA了2发,再打错我就是sb!

1 #include <set> 2 #include <ctime> 3 #include <vector> 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #define N1 400005 8 #define ll long long 9 #define its set<int>::iterator 10 #define itv vector<int>::iterator 11 using namespace std; 12 13 template <typename _T> void read(_T &ret) 14 { 15 ret=0; _T fh=1; char c=getchar(); 16 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 17 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 18 ret=ret*fh; 19 } 20 struct Edge{ 21 int to[N1],nxt[N1],head[N1],cte; ll val[N1]; 22 void ae(int u,int v,ll w) 23 { 24 cte++; to[cte]=v, nxt[cte]=head[u]; 25 head[u]=cte, val[cte]=w; 26 } 27 }e; 28 struct SEG{; //2N*log*2 29 int sum[N1*40],ls[N1*40],rs[N1*40],root[N1],cnt; 30 void pushup(int rt){ sum[rt]=sum[ls[rt]]+sum[rs[rt]]; } 31 void upd(int x,int l,int r,int &rt,int w) 32 { 33 if(!rt) rt=++cnt; 34 // if(cnt>=N1*40) puts("-1"); 35 if(l==r){ sum[rt]+=w; return; } 36 int mid=(l+r)>>1; 37 if(x<=mid) upd(x,l,mid,ls[rt],w); 38 else upd(x,mid+1,r,rs[rt],w); 39 pushup(rt); 40 } 41 int mrg(int r1,int r2) 42 { 43 if(!r1||!r2) return r1+r2; 44 int rt=++cnt; 45 ls[rt]=mrg(ls[r1],ls[r2]); 46 rs[rt]=mrg(rs[r1],rs[r2]); 47 sum[rt]=sum[r1]+sum[r2]; 48 return rt; 49 } 50 int query(int L,int R,int l,int r,int rt) 51 { 52 if(L<=l&&r<=R) return sum[rt]; 53 int mid=(l+r)>>1,ans=0; 54 if(L<=mid) ans+=query(L,R,l,mid,ls[rt]); 55 if(R>mid) ans+=query(L,R,mid+1,r,rs[rt]); 56 return ans; 57 } 58 }s; 59 int n,m; ll L; 60 int dis[N1],disl[N1],ans[N1]; 61 struct node{ int id,p; ll w; }bar[N1]; 62 int cmp(node s1,node s2) 63 { 64 if(s1.w!=s2.w) return s1.w<s2.w; 65 if(s1.p!=s2.p) return s1.p<s2.p; 66 return s1.id<s2.id; 67 } 68 69 void dfs1(int u) 70 { 71 int j,v; 72 bar[u].id=u; bar[u+n].id=u; bar[u].p=0; bar[u+n].p=1; 73 bar[u+n].w=bar[u].w+L; 74 for(j=e.head[u];j;j=e.nxt[j]) 75 { 76 v=e.to[j]; 77 bar[v].w=bar[u].w+e.val[j]; 78 // dis[v]=dis[u]+e.val[j]; 79 dfs1(v); 80 } 81 } 82 void dfs2(int u) 83 { 84 int j,v; 85 s.upd(dis[u],1,m,s.root[u],1); 86 for(j=e.head[u];j;j=e.nxt[j]) 87 { 88 v=e.to[j]; 89 dfs2(v); 90 s.root[u]=s.mrg(s.root[u],s.root[v]); 91 } 92 if(u==1) 93 n=n; 94 ans[u]=s.query(1,disl[u],1,m,s.root[u]); 95 } 96 int main() 97 { 98 read(n); read(L); 99 int x; ll y; 100 for(int i=2;i<=n;i++) read(x), read(y), e.ae(x,i,y); 101 dfs1(1); 102 sort(bar+1,bar+2*n+1,cmp); 103 bar[0].w=-1; 104 for(int i=1;i<=2*n;i++) 105 { 106 if(bar[i].w!=bar[i-1].w) m++; 107 if(!bar[i].p) dis[bar[i].id]=m; 108 else disl[bar[i].id]=m; 109 } 110 dfs2(1); 111 for(int i=1;i<=n;i++) printf("%d ",ans[i]); 112 return 0; 113 }
H:分块
原来想了个拆询问+主席树的log方做法,写了一下发现是假的qwq。
索性暴力分块。不过复习了分块也不错
卡常卡半天我好难,实测块大小为250跑的最快。

1 #include <ctime> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define N1 100005 7 #define M1 N1*4 8 #define ll long long 9 using namespace std; 10 11 template <typename _T> void read(_T &ret) 12 { 13 ret=0; _T fh=1; char c=getchar(); 14 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 15 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 16 ret=ret*fh; 17 } 18 const ll sq=250; 19 20 int getid(int x){ return (x-1)/sq+1; } 21 int n,K,m; 22 int a[N1],tag[N1/sq+5],sum[N1/sq+5][N1]; 23 void pushdown(int id) 24 { 25 if(!tag[id]) return; 26 for(int i=(id-1)*sq+1;i<=id*sq;i++) 27 { 28 sum[id][a[i]]--; 29 (a[i]+=tag[id])%=K; 30 sum[id][a[i]]++; 31 } 32 tag[id]=0; 33 } 34 35 int main() 36 { 37 // freopen("h.txt","r",stdin); 38 freopen("h.in","r",stdin); 39 freopen("h0.out","w",stdout); 40 clock_t sta,ed,tmp; 41 sta=clock(); 42 read(n); read(K); 43 for(int i=1;i<=n;i++) read(a[i]), a[i]=(a[i]%K+K)%K; 44 read(m); 45 int l,r,c,d,ans,lid,rid; 46 for(int i=1;i<=n;i++) 47 { 48 sum[getid(i)][a[i]]++; 49 } 50 for(int k=1;k<=m;k++) 51 { 52 read(l); read(r); read(c); read(d); ans=0; 53 c=(c%K+K)%K; d=(d%K+K)%K; 54 lid=getid(l); rid=getid(r); 55 pushdown(lid); pushdown(rid); 56 if(lid==rid) 57 { 58 for(int i=l;i<=r;i++) 59 { 60 if(a[i]==c) ans++; 61 sum[lid][a[i]]--; (a[i]+=d)%=K; sum[lid][a[i]]++; 62 } 63 }else{ 64 for(int i=l;i<=lid*sq;i++) 65 { 66 if(a[i]==c) ans++; 67 sum[lid][a[i]]--; (a[i]+=d)%=K; sum[lid][a[i]]++; 68 } 69 for(int i=lid+1;i<=rid-1;i++) 70 { 71 ans+=sum[i][(c-tag[i]+K)%K]; 72 (tag[i]+=d)%=K; 73 } 74 for(int i=(rid-1)*sq+1;i<=r;i++) 75 { 76 if(a[i]==c) ans++; 77 sum[rid][a[i]]--; (a[i]+=d)%=K; sum[rid][a[i]]++; 78 } 79 } 80 printf("%d ",ans); 81 } 82 // ed=clock(); printf("%.7lf s ",(double)(ed-sta)/CLOCKS_PER_SEC); 83 return 0; 84 }
I:种类并查集
竟然不会了我太菜了,复习一波并查集拓展做法√
种类并查集板子题,和NOI2001 食物链一个做法。开三倍的n记录点和点之间的关系
并查集不能维护删边,所以我们倒着遍历询问变成了加边。注意删除同一个操作时需要在第一次(倒着看就是最后一次)删除的时候加边!

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int N1=100005; 6 const int M1=1000005; 7 8 template <typename _T> void read(_T &ret) 9 { 10 ret=0; _T fh=1; char c=getchar(); 11 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 12 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 13 ret=ret*fh; 14 } 15 16 int n,K,m; 17 int fa[N1*3]; 18 // vector<int>son[N1]; 19 int used[M1],prt[M1],cnt; 20 struct comd{ int p,x,y; }des[M1],ques[M1]; 21 22 int findfa(int x) 23 { 24 int y=x,t; 25 while(fa[y]!=y) y=fa[y]; 26 while(fa[x]!=y){ t=fa[x], fa[x]=y, x=t; } 27 return y; 28 } 29 int mrg(int x,int y) 30 { 31 int fx=findfa(x), fy=findfa(y); 32 if(fx==fy) return fx; 33 fa[fy]=fx; 34 return fx; 35 } 36 int check(int x,int y) 37 { 38 if(findfa(x)==findfa(y)) return 1; 39 return 0; 40 } 41 42 43 int main() 44 { 45 freopen("a.in","r",stdin); 46 scanf("%d%d%d",&n,&K,&m); 47 char str[10]; 48 for(int i=1;i<=K;i++) read(des[i].p), read(des[i].x), read(des[i].y); 49 for(int i=1;i<=m;i++) 50 { 51 scanf("%s",str); 52 if(str[0]=='q'){ 53 ques[i].p=1; read(ques[i].x); read(ques[i].y); 54 }else{ 55 ques[i].p=2; read(ques[i].x); 56 if(!used[ques[i].x]) used[ques[i].x]=i; 57 } 58 } 59 for(int i=1;i<=3*n;i++) fa[i]=i; 60 int p,x,y,ans; 61 for(int i=1;i<=K;i++) 62 { 63 if(used[i]) continue; 64 p=des[i].p, x=des[i].x, y=des[i].y; 65 if(p==1){ 66 mrg(x,y); mrg(x+n,y+n); mrg(x+2*n,y+2*n); 67 }else{ 68 mrg(x+n,y); mrg(x+2*n,y+n); mrg(x,y+2*n); 69 } 70 } 71 for(int j=m,i;j>=1;j--) 72 { 73 if(ques[j].p==1){ 74 x=ques[j].x, y=ques[j].y; 75 if(check(x,y)&&check(x+n,y+n)&&check(x+2*n,y+2*n)) ans=0; 76 else if(check(x+n,y)&&check(x+2*n,y+n)&&check(x,y+2*n)) ans=1; 77 else if(check(y+n,x)&&check(y+2*n,x+n)&&check(y,x+2*n)) ans=2; 78 else ans=3; 79 prt[++cnt]=ans; 80 // printf("%d ",ans); 81 }else{ 82 i=ques[j].x; 83 if(used[i]!=j) continue; 84 p=des[i].p, x=des[i].x, y=des[i].y; 85 if(p==1){ 86 mrg(x,y); mrg(x+n,y+n); mrg(x+2*n,y+2*n); 87 }else{ 88 mrg(x+n,y); mrg(x+2*n,y+n); mrg(x,y+2*n); 89 } 90 } 91 } 92 for(int i=cnt;i>=1;i--) printf("%d ",prt[i]); 93 return 0; 94 }
J:点分治+01Trie
把点分治给忘了我太弱了555,复习一波点分治板子√
原来想了一个01Trie启发式合并,写了一下发现又是假做法。。
统计链性质的问题多半都能用点分治搞呢
对于每个点的点分树,暴力上01trie求解答案就行了= =
怎么统计?按位分0/1讨论,r的答案减掉l-1的答案,注意l-1可能小于0
转化成如下问题:
已知数列$a$和数列$b$,要求有多少对$ai^bjle maxn$
对$a$建成$01trie$,一次遍历$b$中每个元素求答案
按位从高到低讨论,也就是在$01trie$上走
如果$maxn$某一位是1:
$bj$是0,$ai$这一位填0一定成立,答案加上$sum[ch(x,0)]$。填1不一定成立,需要看后面的位,走向1儿子
$bj$是1,$ai$这一位填1一定成立,答案加上$sum[ch(x,1)]$。填0不一定成立,需要看后面的位,走向0儿子
如果$maxn$某一位是0:
$bj$是0,$ai$这一位只能填0而且需要看后面的位,走向0儿子
$bj$是1,$ai$这一位只能填1而且需要看后面的位,走向1儿子

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 const int maxn=29; 7 const int N1=100005; 8 const int M1=3100005; 9 template <typename _T> void read(_T &ret) 10 { 11 ret=0; _T fh=1; char c=getchar(); 12 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 13 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 14 ret=ret*fh; 15 } 16 17 int n,l,r,de; 18 struct Edge{ 19 int to[N1*2],nxt[N1*2],head[N1],cte; 20 void ae(int u,int v) 21 {cte++; to[cte]=v, nxt[cte]=head[u], head[u]=cte; } 22 }e; 23 struct Trie{ 24 int sum[M1],ch[M1][2],cnt; 25 void clr() 26 { 27 for(int i=1;i<=cnt;i++) sum[i]=ch[i][0]=ch[i][1]=0; 28 cnt=1; 29 } 30 void upd(int w) 31 { 32 int p,x=1; 33 for(int i=maxn;i>=0;i--) 34 { 35 p=(w>>i)&1; 36 if(!ch[x][p]) ch[x][p]=++cnt; 37 x=ch[x][p]; sum[x]++; 38 } 39 } 40 int query(int w,int m) 41 { 42 int ans=0,x=1; 43 if(m<0) return 0; 44 for(int i=maxn;i>=0;i--) 45 { 46 if((m>>i)&1){ 47 if((w>>i)&1) ans+=sum[ch[x][1]], x=ch[x][0]; // w 1 r 1 48 else ans+=sum[ch[x][0]], x=ch[x][1]; // w 0 r 1 49 if(!i) ans+=sum[x]; 50 }else{ 51 if((w>>i)&1) x=ch[x][1]; // w 1 r 0 52 else x=ch[x][0]; // w 0 r 0 53 if(!i) ans+=sum[x]; 54 } 55 } 56 return ans; 57 } 58 }t; 59 60 int used[N1],sz[N1],ms[N1],tsz,G; 61 void gra(int u,int dad) 62 { 63 sz[u]=1; ms[u]=0; 64 for(int j=e.head[u];j;j=e.nxt[j]) 65 { 66 int v=e.to[j]; 67 if(v==dad||used[v]) continue; 68 gra(v,u); sz[u]+=sz[v]; 69 ms[u]=max(ms[u],sz[v]); 70 } 71 ms[u]=max(ms[u],tsz-sz[u]); 72 if(ms[u]<ms[G]) G=u; 73 } 74 int a[N1],sa0[N1],sa1[N1]; 75 void dfs0(int u,int dad) 76 { 77 for(int j=e.head[u];j;j=e.nxt[j]) 78 { 79 int v=e.to[j]; if(v==dad||used[v]) continue; 80 sa0[v]=sa0[u]^a[v]; sa1[v]=sa1[u]^a[v]; 81 dfs0(v,u); 82 } 83 } 84 ll dfs1(int u,int dad) //calc 85 { 86 ll ans=t.query(sa1[u],r); 87 ans-=t.query(sa1[u],l-1); 88 for(int j=e.head[u];j;j=e.nxt[j]) 89 { 90 int v=e.to[j]; 91 if(v==dad||used[v]) continue; 92 ans+=dfs1(v,u); 93 } 94 return ans; 95 } 96 void dfs2(int u,int dad) //update in trie 97 { 98 t.upd(sa0[u]); 99 for(int j=e.head[u];j;j=e.nxt[j]) 100 { 101 int v=e.to[j]; 102 if(v==dad||used[v]) continue; 103 dfs2(v,u); 104 } 105 } 106 ll calc(int u) 107 { 108 sa0[u]=a[u]; sa1[u]=0; dfs0(u,-1); 109 t.upd(sa0[u]); 110 ll ans=0; 111 for(int j=e.head[u];j;j=e.nxt[j]) 112 { 113 int v=e.to[j]; if(used[v]) continue; 114 ans+=dfs1(v,u); 115 dfs2(v,u); 116 } 117 t.clr(); 118 return ans; 119 } 120 ll main_dfs(int u) 121 { 122 ll ans=0; 123 used[u]=1; ans+=calc(u); 124 for(int j=e.head[u];j;j=e.nxt[j]) 125 { 126 int v=e.to[j]; if(used[v]) continue; 127 G=0; tsz=sz[v]; gra(v,u); 128 ans+=main_dfs(G); 129 } 130 return ans; 131 } 132 133 int main() 134 { 135 freopen("a.in","r",stdin); 136 int x,y; 137 scanf("%d%d%d",&n,&l,&r); 138 for(int i=1;i<=n;i++) read(a[i]); 139 for(int i=1;i<n;i++) read(x), read(y), e.ae(x,y), e.ae(y,x); 140 ms[0]=tsz=n; gra(1,-1); gra(G,-1); 141 t.clr(); 142 ll ans=main_dfs(G); 143 printf("%lld ",ans); 144 return 0; 145 }
K:贪心+单调栈
把问题想象成把一个个元素放到后面填上去
如果当前元素$a[i]$小于答案序列的最后一个元素$b[j]$,这个位置放$a[i]$会更优秀,但必须保证序列合法。
那么如果$b[j]$可以被删除,仅当$i$小于$b[j]$在原序列中最后一次出现的位置
如果$a[i]$大于等于$b[j]$就没有删除的必要了
可能有多个$b[j]$被同时删除,用单调栈维护答案序列即可

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 const int N1=1000005; 7 8 template <typename _T> void read(_T &ret) 9 { 10 ret=0; _T fh=1; char c=getchar(); 11 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 12 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 13 ret=ret*fh; 14 } 15 16 int n,K; 17 int a[N1],la[N1],used[N1]; 18 int stk[N1],tp; 19 20 int main() 21 { 22 freopen("a.in","r",stdin); 23 scanf("%d%d",&n,&K); 24 for(int i=1;i<=n;i++) read(a[i]), la[a[i]]=i; 25 for(int i=1;i<=K;i++) 26 { 27 if(!la[i]){ 28 puts("Kanade"); return 0; 29 } 30 } 31 for(int i=1;i<=n;i++) 32 { 33 if(used[a[i]]) continue; 34 while(tp>0&&a[i]<stk[tp]) 35 { 36 if(i<la[stk[tp]]) used[stk[tp]]=0, stk[tp]=0, tp--; 37 else break; 38 } 39 stk[++tp]=a[i]; used[a[i]]=1; 40 } 41 for(int i=1;i<=tp;i++) printf("%d ",stk[i]); 42 puts(""); 43 return 0; 44 }
L:LCT维护树的连通性
复习板子很开心
既然是板子题,没啥可解释的

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 const int N1=100005; 7 8 template <typename _T> void read(_T &ret) 9 { 10 ret=0; _T fh=1; char c=getchar(); 11 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 12 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 13 ret=ret*fh; 14 } 15 16 struct LCT{ 17 int fa[N1],ch[N1][2],rev[N1]; 18 int idf(int x){ return ch[fa[x]][0]==x?0:1; } 19 int isroot(int x){ return (ch[fa[x]][0]==x||ch[fa[x]][1]==x)?0:1; } 20 void revers(int x){ swap(ch[x][0],ch[x][1]); rev[x]^=1; } 21 void pushdown(int x){ if(rev[x]) revers(ch[x][0]), revers(ch[x][1]), rev[x]^=1; } 22 void rot(int x) 23 { 24 int y=fa[x],ff=fa[y],px=idf(x),py=idf(y); 25 if(!isroot(y)) ch[ff][py]=x; fa[x]=ff; 26 fa[ch[x][px^1]]=y, ch[y][px]=ch[x][px^1]; 27 ch[x][px^1]=y; fa[y]=x; 28 // pushup(y), pushup(x); 29 } 30 int stk[N1],tp; 31 void splay(int x) 32 { 33 int y=x; stk[++tp]=x; 34 while(!isroot(y)){ stk[++tp]=fa[y],y=fa[y]; } 35 while(tp){ pushdown(stk[tp--]); } 36 while(!isroot(x)) 37 { 38 y=fa[x]; 39 if(isroot(y)) rot(x); 40 else if(idf(y)==idf(x)) rot(y),rot(x); 41 else rot(x),rot(x); 42 } 43 } 44 void access(int x){ for(int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y; } // pushup(x); 45 void mkroot(int x){ access(x),splay(x),revers(x); } 46 void split(int x,int y){ mkroot(x),access(y),splay(y); } 47 int fdroot(int x){ access(x),splay(x); while(ch[x][0]) pushdown(ch[x][0]),x=ch[x][0]; return x; } 48 void cut(int x,int y){ split(x,y); fa[x]=ch[y][0]=0; } //pushup(y); 49 void link(int x,int y){ split(x,y); fa[x]=y; } 50 int isconn(int x,int y){ mkroot(x); return fdroot(y)==x?1:0; } 51 }s; 52 53 int n,m,de; 54 55 int main() 56 { 57 freopen("a.in","r",stdin); 58 read(n), read(m); 59 int x,y,ans; char str[10]; 60 for(int i=1;i<=m;i++) 61 { 62 scanf("%s",str); read(x), read(y); 63 if(str[0]=='+') s.link(x,y); 64 if(str[0]=='-') s.cut(x,y); 65 if(str[0]=='?') 66 { 67 ans=s.isconn(x,y); 68 if(ans) puts("Yes"); 69 else puts("No"); 70 } 71 } 72 return 0; 73 }
M:待填坑(在学KD树)
N:树链剖分
又是一道板子题,把树剖复习了

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 const int N1=100005; 7 const int p=170001; 8 9 template <typename _T> void read(_T &ret) 10 { 11 ret=0; _T fh=1; char c=getchar(); 12 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 13 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 14 ret=ret*fh; 15 } 16 struct Edge{ 17 int to[N1*2],nxt[N1*2],head[N1],cte; 18 void ae(int u,int v) 19 { cte++; to[cte]=v, nxt[cte]=head[u], head[u]=cte; } 20 }e; 21 struct SEG{ 22 int sum[N1<<2],tag[N1<<2]; //****二倍 23 void pushdown(int l,int r,int rt) 24 { 25 if(!tag[rt]) return; 26 int mid=(l+r)>>1; 27 (sum[rt<<1]+=1ll*tag[rt]*(mid-l+1)%p)%=p; 28 (sum[rt<<1|1]+=1ll*tag[rt]*(r-mid)%p)%=p; 29 (tag[rt<<1]+=tag[rt])%=p; //***标记也要更新 30 (tag[rt<<1|1]+=tag[rt])%=p; 31 tag[rt]=0; 32 } 33 void pushup(int rt){ sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%p; } 34 void upd(int L,int R,int l,int r,int rt,int w) 35 { 36 if(L<=l&&r<=R) 37 { 38 (sum[rt]+=1ll*w*(r-l+1)%p)%=p; 39 (tag[rt]+=w)%=p; 40 return; 41 } 42 int mid=(l+r)>>1; pushdown(l,r,rt); 43 if(L<=mid) upd(L,R,l,mid,rt<<1,w); 44 if(R>mid) upd(L,R,mid+1,r,rt<<1|1,w); 45 pushup(rt); 46 } 47 int query(int L,int R,int l,int r,int rt) 48 { 49 if(L<=l&&r<=R){ return sum[rt]; } 50 int mid=(l+r)>>1,ans=0; pushdown(l,r,rt); 51 if(L<=mid) (ans+=query(L,R,l,mid,rt<<1))%=p; 52 if(R>mid) (ans+=query(L,R,mid+1,r,rt<<1|1))%=p; 53 return ans; 54 } 55 }s; 56 57 int n,m,de; 58 int pos[N1],sz[N1],son[N1],fa[N1],dep[N1],tp[N1],tot; 59 void dfs1(int u,int dad) 60 { 61 sz[u]=1; 62 for(int j=e.head[u];j;j=e.nxt[j]) 63 { 64 int v=e.to[j]; if(v==dad) continue; 65 fa[v]=u, dep[v]=dep[u]+1; 66 dfs1(v,u); sz[u]+=sz[v]; 67 son[u]=sz[v]>sz[son[u]]?v:son[u]; 68 } 69 } 70 void dfs2(int u) 71 { 72 pos[u]=++tot; 73 if(son[u]) tp[son[u]]=tp[u], dfs2(son[u]); 74 for(int j=e.head[u];j;j=e.nxt[j]) 75 { 76 int v=e.to[j]; 77 if(v==fa[u]||v==son[u]) continue; 78 tp[v]=v; dfs2(v); 79 } 80 } 81 void update(int x,int y,int w) 82 { 83 while(tp[x]!=tp[y]) 84 { 85 if(dep[tp[x]]<dep[tp[y]]) swap(x,y); 86 s.upd(pos[tp[x]],pos[x],1,n,1,w); 87 x=fa[tp[x]]; 88 } 89 if(dep[x]>dep[y]) swap(x,y); 90 s.upd(pos[x],pos[y],1,n,1,w); 91 } 92 int query(int x,int y) 93 { 94 int ans=0; 95 while(tp[x]!=tp[y]) 96 { 97 if(dep[tp[x]]<dep[tp[y]]) swap(x,y); 98 (ans+=s.query(pos[tp[x]],pos[x],1,n,1))%=p; 99 x=fa[tp[x]]; 100 } 101 if(dep[x]>dep[y]) swap(x,y); 102 (ans+=s.query(pos[x],pos[y],1,n,1))%=p; 103 return ans; 104 } 105 int a[N1]; 106 107 int main() 108 { 109 freopen("a.in","r",stdin); 110 read(n); read(m); int q,x,y,z,ans; 111 for(int i=1;i<=n;i++) read(a[i]); 112 for(int i=1;i<n;i++) read(x), read(y), e.ae(x,y), e.ae(y,x); 113 dep[1]=1; dfs1(1,-1); 114 tp[1]=1; dfs2(1); 115 for(int i=1;i<=n;i++) s.upd(pos[i],pos[i],1,n,1,a[i]); 116 for(int i=1;i<=m;i++) 117 { 118 read(q); 119 if(q==1){ 120 read(x), read(y), read(z); 121 update(x,y,z); 122 }else{ 123 read(x), read(y); 124 ans=query(x,y); 125 printf("%d ",ans); 126 } 127 } 128 return 0; 129 } 130 131 // int lca(int x,int y) 132 // { 133 // while(tp[x]!=tp[y]) 134 // { 135 // if(dep[tp[x]]<dep[tp[y]]) swap(x,y); 136 // x=fa[tp[x]]; 137 // } 138 // return dep[x]<dep[y]?x:y; 139 // }
O:线段树上二分
又又是一道板子题
线段树维护区间最大值
询问怎么处理?询问的区间一定挂在$O(logn)$个线段树区间上,从左到右依次考虑每个线段树区间内是否存在某点符合要求,符合要求就在这个线段树区间内二分,总复杂度$O(mlogn)$

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 using namespace std; 6 const int N1=2000005; 7 const int inf=0x3f3f3f3f; 8 9 template <typename _T> void read(_T &ret) 10 { 11 ret=0; _T fh=1; char c=getchar(); 12 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 13 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 14 ret=ret*fh; 15 } 16 17 int n,m; 18 int a[N1]; 19 20 struct SEG{ 21 int ma[N1<<2]; 22 void pushup(int rt){ ma[rt]=max(ma[rt<<1],ma[rt<<1|1]); } 23 void upd(int x,int l,int r,int rt,int w) 24 { 25 if(l==r){ ma[rt]=w; return; } 26 int mid=(l+r)>>1; 27 if(x<=mid) upd(x,l,mid,rt<<1,w); 28 else upd(x,mid+1,r,rt<<1|1,w); 29 pushup(rt); 30 } 31 int found,ans; 32 void find(int l,int r,int rt,int w) 33 { 34 if(l==r){ ans=l; return; } 35 int mid=(l+r)>>1; 36 if(ma[rt<<1]>=w) find(l,mid,rt<<1,w); 37 else find(mid+1,r,rt<<1|1,w); 38 } 39 void qlst(int L,int R,int l,int r,int rt,int w) 40 { 41 if(L<=l&&r<=R){ 42 if(ma[rt]>=w&&!found) find(l,r,rt,w), found=1; 43 return ; 44 } 45 int mid=(l+r)>>1; 46 if(L<=mid&&!found) qlst(L,R,l,mid,rt<<1,w); 47 if(R>mid&&!found) qlst(L,R,mid+1,r,rt<<1|1,w); 48 } 49 int query(int L,int R,int w) 50 { 51 found=0, ans=-1; 52 qlst(L,R,1,n,1,w); 53 return ans; 54 } 55 }s; 56 57 int main() 58 { 59 read(n); int x,y,ans; 60 for(int i=1;i<=n;i++) read(a[i]), s.upd(i,1,n,1,a[i]); 61 read(m); 62 for(int i=1;i<=m;i++) 63 { 64 read(x), read(y); x++, y=y-i+1; 65 ans=s.query(x,n,y); 66 if(ans==-1&&x>1) ans=s.query(1,x-1,y); 67 if(ans==-1){ puts("-1"); continue; } 68 if(ans>=x) printf("%d ",ans-x); 69 else printf("%d ",n-(x-ans)); 70 s.upd(ans,1,n,1,-inf); 71 } 72 return 0; 73 }
P:线段树+打表
好题,赞
一开始觉得线段树做不了写了个分块做法,卡了好久的常还是T。。看了眼题解,原来线段树可以做啊qwq
首先推一下公式,我们有$x^{varphi (n)}=1(modspace p)$
那么$x^{yspace modspacevarphi (p)}=1(modspace p)$,且$varphi (p)=p-1$
现在只考虑指数$5^{k}space modspace (p-1)$,然后怎么办?似乎并没有什么可做的手段了啊
打表发现$5^{k}space modspace (p-1)$的值竟然以27为循环节!
那么问题转化成,现在有一个序列,每个位置有27种取值,取值构成一个环。每次修改让一段区间内的所有位置都取下一个值
考虑用线段树维护,每个线段树区间根据其子区间的信息也可以得到27种取值
还需要记录$pos$,表示当前线段树区间取的是第几个位置的值,再用标记$tag$维护$pos$数组

1 #include <ctime> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #pragma GCC optimize(3) 6 #define ll long long 7 using namespace std; 8 const int N1=200005; 9 const int ma=27; 10 const int p=73156157; 11 const int phip=73156156; 12 13 template <typename _T> void read(_T &ret) 14 { 15 ret=0; _T fh=1; char c=getchar(); 16 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 17 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 18 ret=ret*fh; 19 } 20 ll qpow(ll x,ll y) 21 { 22 ll ans=1; 23 for(;y;x=x*x%p,y>>=1) if(y&1) ans=ans*x%p; 24 return ans; 25 } 26 inline ll pow5(int x){ return 1ll*x*x%p*x%p*x%p*x%p; } 27 28 int n,m,de; 29 int a[N1]; 30 31 struct SEG{ 32 int sum[N1<<2][27],tag[N1<<2],pos[N1<<2]; 33 void pushdown(int rt) 34 { 35 if(!tag[rt]) return; 36 (pos[rt<<1]+=tag[rt])%=ma, (pos[rt<<1|1]+=tag[rt])%=ma; 37 (tag[rt<<1]+=tag[rt])%=ma, (tag[rt<<1|1]+=tag[rt])%=ma; 38 pos[rt]=tag[rt]=0; 39 } 40 void pushup(int rt) 41 { 42 for(int j=0;j<ma;j++) 43 sum[rt][j]=(sum[rt<<1][(j+pos[rt<<1])%ma]+sum[rt<<1|1][(j+pos[rt<<1|1])%ma])%p; 44 } 45 void add(int L,int R,int l,int r,int rt) 46 { 47 if(L<=l&&r<=R){ (pos[rt]+=1)%=ma; (tag[rt]+=1)%=ma; return; } 48 int mid=(l+r)>>1; pushdown(rt); 49 if(L<=mid) add(L,R,l,mid,rt<<1); 50 if(R>mid) add(L,R,mid+1,r,rt<<1|1); 51 pushup(rt); 52 } 53 int query(int L,int R,int l,int r,int rt) 54 { 55 if(L<=l&&r<=R){ return sum[rt][pos[rt]]; } 56 int mid=(l+r)>>1,ans=0; pushdown(rt); 57 if(L<=mid) (ans+=query(L,R,l,mid,rt<<1))%=p; 58 if(R>mid) (ans+=query(L,R,mid+1,r,rt<<1|1))%=p; 59 pushup(rt); 60 return ans; 61 } 62 void build(int l,int r,int rt) 63 { 64 if(l==r){ 65 sum[rt][0]=a[l]; 66 for(int j=1;j<ma;j++) sum[rt][j]=pow5(sum[rt][j-1]); 67 return; 68 } 69 int mid=(l+r)>>1; 70 build(l,mid,rt<<1); 71 build(mid+1,r,rt<<1|1); 72 pushup(rt); 73 } 74 }s; 75 76 int main() 77 { 78 freopen("a.in","r",stdin); 79 // freopen("a.out","w",stdout); 80 // clock_t sta,ed; 81 // sta=clock(); 82 read(n); read(m); 83 int fl,x,y; ll ans; 84 for(int i=1;i<=n;i++) read(a[i]); 85 s.build(1,n,1); 86 for(int j=1;j<=m;j++) 87 { 88 read(fl), read(x), read(y); 89 if(fl==1){ 90 s.add(x,y,1,n,1); 91 }else{ 92 ans=0; 93 ans=s.query(x,y,1,n,1); 94 ans=ans*qpow(y-x+1,p-2)%p; 95 printf("%lld ",ans); 96 } 97 } 98 // ed=clock(); printf("%.7lf s ",(double)(ed-sta)/CLOCKS_PER_SEC); 99 // printf("%.7lf s ",(double)(tot)/CLOCKS_PER_SEC); 100 return 0; 101 }