2017-4-13校内训练
坑题+SB数据结构训练赛
A.题目大意:某个地方有五个城市,给出十条单向边的长度,分别为1~2,1~3,2~3,2~4,3~4,3~5,4~5,4~1,5~1,5~2,问从一号城开始走,走长度恰好为n,走的城市数最少且字典序最小的方案。(1<=边长<=n<=500,000)
思路:以在第i个城市走了长度为j为状态做bfs,字典序小的先拓展,复杂度O(15n)。
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return x; } #define MN 500000 int d[5][2],u[5][MN+5],rx[5][MN+5],ry[5][MN+5],qx[5*MN+5],qy[5*MN+5],qn; void out(int x,int y) { if(y)out(rx[x][y],ry[x][y]); printf("%d ",x+1); } int main() { int n=read(),i,x,y,xx,yy,a,b; for(i=0;i<5;++i)d[i][0]=read(),d[i][1]=read(); for(i=0;i<=qn;++i) { x=qx[i];y=qy[i]; if(y==n){out(x,y);return 0;} a=1;b=2;if((x+a)%5>(x+b)%5)swap(a,b); xx=(x+a)%5;yy=y+d[x][a-1]; if(yy<=n&&!u[xx][yy])++qn,u[qx[qn]=xx][qy[qn]=yy]=1,rx[xx][yy]=x,ry[xx][yy]=y; xx=(x+b)%5;yy=y+d[x][b-1]; if(yy<=n&&!u[xx][yy])++qn,u[qx[qn]=xx][qy[qn]=yy]=1,rx[xx][yy]=x,ry[xx][yy]=y; } puts("-1"); }
B.题目大意:给出一个长度为n的序列,问有多少子串的乘积为完全平方数。(n<=500,000,数字大小<=10^6)
思路:分解质因数后各质因子个数均为偶数即为完全平方数,那么同种质因子每有一个就异或上一个1,各种质因子异或出的数都为0即为完全平方数,按这个思路求一个前缀异或和,两个相同的前缀异或和即对应一个乘积为完全平方数的子串,hash一下上map就没了,复杂度O(nlogn)。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<map> using namespace std; #define ll long long inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return x; } #define MX 1000000 map<ll,int> mp[3]; const int magic[3]={31,37,233}; ll f[3][MX+5],ff[3]; int u[MX+5],uu[MX+5],p[MX+5],pn; void push(int x) { if(uu[x]^=1)for(int i=0;i<3;++i)ff[i]+=f[i][x]; else for(int i=0;i<3;++i)ff[i]-=f[i][x]; } int main() { int n=read(),i,j;ll ans=0; for(i=2;i<=MX;++i) { if(!u[i])p[++pn]=i,u[i]=i; for(j=1;i*p[j]<=MX;++j){u[i*p[j]]=p[j];if(i%p[j]==0)break;} } for(i=0;i<3;++i)for(f[i][0]=j=1;j<=MX;++j)f[i][j]=f[i][j-1]*magic[i]; for(i=0;i<3;++i)++mp[i][0]; while(n--) { for(i=read();i>1;i/=u[i])push(u[i]); for(i=0,j=MX;i<3;++i)j=min(j,mp[i][ff[i]]++); ans+=j; } cout<<ans; }
C.题目大意:一个n*m的棋盘,一个棋子一开始在(i,j),每次若在(x,y)能移动到(x-a,y-b),(x+a,y-b),(x-a,y+b),(x+a,y+b)中的一格,问至少几步能移动到(1,1),(1,m),(n,1),(n,m)中的其中一格。(n,m<=10^6)
思路:x,y坐标相互独立,可以先分别求出两种坐标的最小步数,如果一个先到就让另一个反复横跳,注意特判各种无解情况,复杂度O(1)。
#include<cstdio> #include<algorithm> using namespace std; #define INF 1000000000 int n,m,ans=INF,a,b; void cal(int x,int y,int tx,int ty) { if((tx-x)%a||(ty-y)%b)return; int px=(tx-x)/a,py=(ty-y)/b; if(px<0)px=-px;if(py<0)py=-py; if((px-py)%2)return; if(px==py){ans=min(ans,px);return;} if(px<py&&x-a<1&&x+a>n)return; if(py<px&&y-b<1&&y+b>m)return; ans=min(ans,max(px,py)); } int main() { int i,j; scanf("%d%d%d%d%d%d",&n,&m,&i,&j,&a,&b); cal(i,j,1,1);cal(i,j,1,m); cal(i,j,n,1);cal(i,j,n,m); printf(ans<INF?"%d":"Poor Inna and pony!",ans); }
D.题目大意:一个长度为n的小写字母串,支持两种操作:1.将一个子串的所有字母在字母表上循环后移k位(例如a后移2位是c,z后移两位是b);2.查询一个子串内有多少子序列可以通过重组得到一个回文串。(n,操作数<=10^5)
思路:直接用线段树维护区间内各字母出现次数,每次O(26)更新信息,查询时先查出区间内各字母出现次数,枚举一个出现奇数次的字母或全是偶数次的,f1[i]表示i个字母中选出奇数个的方案,f2[i]表示i个字母中选出偶数个的方案,则f1[0]=0,f2[0]=1,每多一个字母我们都可以选或不选,则f1[i]=f2[i]=f1[i-1]+f2[i-1](i>0)(事实上都是2的次幂),然后很容易即可统计答案,复杂度O(26nlogn)。
#include<cstdio> inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return x; } #define MN 100000 #define L (k<<1) #define R (k<<1|1) #define MOD 1000000007 struct node{int l,r,s[26],mk;}t[MN*4+5]; int cnt[26],f1[MN+5],f2[MN+5],lf[26],rf[26]; char s[MN+5]; inline void up(int k){for(int i=0;i<26;++i)t[k].s[i]=t[L].s[i]+t[R].s[i];} inline void mark(int k,int x) { int i,j,s[26]; for(i=0;i<26;++i)s[i]=t[k].s[i]; for(i=0,j=x;i<26;++i,++j>25?j=0:0)t[k].s[j]=s[i]; if((t[k].mk+=x)>25)t[k].mk-=26; } inline void down(int k){if(t[k].mk)mark(L,t[k].mk),mark(R,t[k].mk),t[k].mk=0;} void build(int k,int l,int r) { if((t[k].l=l)==(t[k].r=r)){++t[k].s[s[l]-'a'];return;} int mid=l+r>>1; build(L,l,mid);build(R,mid+1,r);up(k); } void add(int k,int l,int r,int x) { if(t[k].l==l&&t[k].r==r){mark(k,x%26);return;} int mid=t[k].l+t[k].r>>1;down(k); if(r<=mid)add(L,l,r,x); else if(l>mid)add(R,l,r,x); else add(L,l,mid,x),add(R,mid+1,r,x); up(k); } void query(int k,int l,int r) { if(t[k].l==l&&t[k].r==r){for(int i=0;i<26;++i)cnt[i]+=t[k].s[i];return;} int mid=t[k].l+t[k].r>>1;down(k); if(r<=mid)query(L,l,r); else if(l>mid)query(R,l,r); else query(L,l,mid),query(R,mid+1,r); } int main() { int n=read(),m=read(),t,l,r,i,ans; for(f2[0]=i=1;i<=n;++i)f1[i]=(f1[i-1]+f2[i-1])%MOD,f2[i]=(f1[i-1]+f2[i-1])%MOD; scanf("%s",s+1); build(1,1,n); while(m--) { t=read();l=read()+1;r=read()+1; if(t==1)add(1,l,r,read()); else { for(i=0;i<26;++i)cnt[i]=0;query(1,l,r); for(i=0;i<26;++i)lf[i]=1LL*(i?lf[i-1]:1)*f2[cnt[i]]%MOD; for(i=26;i--;)rf[i]=1LL*(i<25?rf[i+1]:1)*f2[cnt[i]]%MOD; for(ans=rf[0]-1,i=0;i<26;++i) ans=(ans+1LL*(i?lf[i-1]:1)*(i<25?rf[i+1]:1)%MOD*f1[cnt[i]])%MOD; printf("%d\n",ans); } } }
E.题目大意:询问[a,b]内有多少个数字是k的倍数。(1<=k<=10^18,-10^18<=a<=b<=10^18)
思路:相信是个人都会做,乱写写呗,懒得判各种情况就写了奇怪的代码。
#include<cstdio> #include<iostream> using namespace std; int main() { long long k,a,b,da,db,i; cin>>k>>a>>b; da=(a/k-5)*k; while(da<a)da+=k; db=(b/k+5)*k; while(db>b)db-=k; cout<<(db-da)/k+1; }
F.题目大意:给出一棵n个节点的有根树,要求支持将一棵子树内所有点的权值加上V+d*K(其中d为该点到子树根的距离)和查询一条链上的权值和。(n<=10^5)
思路:树剖一下,线段树多维护一个子树深度和,然后就能维护了,复杂度O(nlogn^2)。
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return x; } #define MN 100000 #define L (k<<1) #define R (k<<1|1) #define MOD 1000000007 struct node{int l,r,s,ds,mk1,mk2;}t[MN*4+5]; struct edge{int nx,t;}e[MN*2+5]; int h[MN+5],en,fa[MN+5],d[MN+5],a[MN+5],s[MN+5],mx[MN+5],f[MN+5],dl[MN+5],dr[MN+5],cnt; inline void ins(int x,int y) { e[++en]=(edge){h[x],y};h[x]=en; e[++en]=(edge){h[y],x};h[y]=en; } void pre(int x) { s[x]=1; for(int i=h[x];i;i=e[i].nx)if(e[i].t!=fa[x]) { fa[e[i].t]=x;d[e[i].t]=d[x]+1; pre(e[i].t); s[x]+=s[e[i].t];if(s[e[i].t]>s[mx[x]])mx[x]=e[i].t; } } void dfs(int x,int k) { a[dl[x]=++cnt]=d[x];f[x]=k; if(mx[x])dfs(mx[x],k); for(int i=h[x];i;i=e[i].nx) if(e[i].t!=mx[x]&&e[i].t!=fa[x])dfs(e[i].t,e[i].t); dr[x]=cnt; } inline void up(int k){t[k].s=(t[L].s+t[R].s)%MOD;} inline void mark(int k,int x1,int x2) { t[k].s=(t[k].s+1LL*x1*(t[k].r-t[k].l+1)+1LL*x2*t[k].ds)%MOD; t[k].mk1=(t[k].mk1+x1)%MOD; t[k].mk2=(t[k].mk2+x2)%MOD; } inline void down(int k) { mark(L,t[k].mk1,t[k].mk2);mark(R,t[k].mk1,t[k].mk2); t[k].mk1=t[k].mk2=0; } void build(int k,int l,int r) { if((t[k].l=l)==(t[k].r=r)){t[k].ds=a[l];return;} int mid=l+r>>1; build(L,l,mid);build(R,mid+1,r); up(k);t[k].ds=(t[L].ds+t[R].ds)%MOD; } void add(int k,int l,int r,int x1,int x2) { if(t[k].l==l&&t[k].r==r){mark(k,x1,x2);return;} int mid=t[k].l+t[k].r>>1;down(k); if(r<=mid)add(L,l,r,x1,x2); else if(l>mid)add(R,l,r,x1,x2); else add(L,l,mid,x1,x2),add(R,mid+1,r,x1,x2); up(k); } int query(int k,int l,int r) { if(t[k].l==l&&t[k].r==r)return t[k].s; int mid=t[k].l+t[k].r>>1;down(k); if(r<=mid)return query(L,l,r); if(l>mid)return query(R,l,r); return (query(L,l,mid)+query(R,mid+1,r))%MOD; } int query(int x,int y) { int res=0; for(;f[x]!=f[y];x=fa[f[x]]) { if(d[f[x]]<d[f[y]])swap(x,y); res=(res+query(1,dl[f[x]],dl[x]))%MOD; } if(dl[x]>dl[y])swap(x,y); return ((res+query(1,dl[x],dl[y]))%MOD+MOD)%MOD; } int main() { int n,m,rt,i,x,y,z;char o[5]; n=read();m=read();rt=read(); for(i=1;i<n;++i)ins(read(),read()); pre(rt);dfs(rt,rt); build(1,1,n); while(m--) { scanf("%s",o);x=read();y=read(); if(o[0]=='U')z=read(),add(1,dl[x],dr[x],(y-1LL*d[x]*z)%MOD,z); else printf("%d\n",query(x,y)); } }