TZOJ 挑战题库随机训练04
Posted taozi1115402474
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TZOJ 挑战题库随机训练04相关的知识,希望对你有一定的参考价值。
A.C++实验:日期相减回到顶部
题意
给一个日期求前num天的日期
题解
每次-1天,判断一下闰年就行,复杂度O(num)
需要重载+号和输出<<号
代码
1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 using namespace std; 5 6 struct Date{ 7 int y,m,d; 8 Date(int _y=0,int _m=0,int _d=0):y(_y),m(_m),d(_d){} 9 }; 10 Date operator-(Date a,int num){ 11 while(num--){ 12 if(a.d>1)a.d--; 13 else if(a.d==1){ 14 if(a.m>1){ 15 if(a.m==2||a.m==4||a.m==6||a.m==8||a.m==9||a.m==11){ 16 a.m--;a.d=31; 17 }else if(a.m==3){ 18 if(a.y%400==0||(a.y%4==0&&a.y%100!=0)){ 19 a.m--;a.d=29; 20 }else{ 21 a.m--;a.d=28; 22 } 23 }else{ 24 a.m--;a.d=30; 25 } 26 } 27 else if(a.m==1){ 28 a.y--;a.m=12;a.d=31; 29 } 30 } 31 } 32 return a; 33 } 34 ostream& operator<<(ostream &out,Date s) 35 { 36 cout<<s.y<<"-"<<s.m<<"-"<<s.d; 37 return out; 38 }
B.Fibnacci Numbers回到顶部
题意
f[1]=1,f[2]=2,f[i]=f[i-1]+f[i-2]
s[n]=Σf[i]^2
给定a,n,x,1 ≤ a ≤ 100000000; 1 ≤ x ≤ 1000000000; 2 ≤ n ≤ 100000000
求ans=(a^s[x])%n
题解
找规律得到s[x]=f[x]*f[x+1],式子变成ans=(a^(f[x]*f[x+1]))%n
模数不是质数,采用欧拉降幂,式子变成ans=(a^(f[x]*f[x+1]%phi+phi))%n
然后需要求出f[n]*f[n+1]%phi,可以采用矩阵快速幂
构造矩阵[1 1,0 0]*([1 1,1 0]^x)%phi,矩阵快速幂求出[1 1 1 0]^x%phi,最后乘上初始矩阵[1 1 0 0],得到f[x]*f[x+1]%phi
最后通过快速幂求出答案,复杂度O(8logxlogx)
PS:矩阵快速幂%phi不是%n,被坑了一下
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define LL long long 5 LL GetPhi(LL n) 6 { 7 LL m=sqrt(n+0.5); 8 LL ans=n; 9 for(int i=2;i<=m;++i) 10 if(n%i==0){ 11 ans=ans/i*(i-1); 12 while(n%i==0)n/=i; 13 } 14 if(n>1)ans=ans/n*(n-1); 15 return ans; 16 } 17 LL MD,phi; 18 struct mat{ 19 LL a[2][2]; 20 mat(){ 21 memset(a,0,sizeof a); 22 } 23 mat operator*(const mat &b)const{ 24 mat c; 25 for(int i=0;i<2;i++) 26 for(int j=0;j<2;j++) 27 for(int k=0;k<2;k++) 28 c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j]%phi)%phi; 29 return c; 30 } 31 }; 32 mat quick(mat a,LL b){ 33 mat ans; 34 for(int i=0;i<2;i++)ans.a[i][i]=1; 35 while(b){ 36 if(b&1)ans=ans*a; 37 a=a*a; 38 b>>=1; 39 } 40 return ans; 41 } 42 LL quick_LL(LL a,LL b){ 43 LL ans=1; 44 while(b){ 45 if(b&1)ans=ans*a%MD; 46 a=a*a%MD; 47 b>>=1; 48 } 49 return ans; 50 } 51 int main(){ 52 LL a,x; 53 while(scanf("%lld%lld%lld",&a,&x,&MD)!=EOF,a||x||MD){ 54 LL val=0; 55 phi=GetPhi(MD); 56 mat ans; 57 ans.a[0][0]=ans.a[1][0]=ans.a[0][1]=1; 58 ans=quick(ans,x); 59 mat A; 60 A.a[0][0]=A.a[0][1]=1; 61 ans=A*ans; 62 //printf("%lld %lld ",ans.a[0][0],ans.a[0][1]); 63 val=(ans.a[0][0]*ans.a[0][1]%phi-1+phi)%phi; 64 printf("%lld ",quick_LL(a,val%phi+phi)%MD); 65 } 66 return 0; 67 }
C.Circular Sequence回到顶部
题意
n([1,10^5])个数围成一个环,求最大子段和
题解
做过一个简单版的,排成1排的最大字段和
可以求出简单版的最大字段和
如果我们把n个数拉长变成2*n
另一种情况就是跨过n的最大字段和,那我们只需要dp求出到右端点r的最大子段和
dp[i]=max(dp[i-1],PreSum+a[i])
然后枚举左端点,求出右端点,求出[l,n]的和+dp[l]即可,复杂度O(n)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define LL long long 5 const int N=1e5+5; 6 LL a[N],pre[N]; 7 int main(){ 8 int t,n; 9 scanf("%d",&t); 10 while(t--){ 11 scanf("%d",&n); 12 LL preSum=0,maxN=-1e9; 13 for(int i=1;i<=n;i++){ 14 scanf("%lld",&a[i]); 15 maxN=max(maxN,a[i]); 16 preSum+=a[i]; 17 pre[i]=max(pre[i-1],preSum); 18 } 19 LL sum=0,ans=0,f=0; 20 for(int i=1;i<=n;i++){ 21 sum+=a[i]; 22 if(a[i]>=0)f=1; 23 if(sum<0)sum=0; 24 ans=max(ans,sum); 25 if(i<n){ 26 preSum-=a[i]; 27 ans=max(ans,preSum+pre[i]); 28 } 29 } 30 if(f)printf("%lld ",ans); 31 else printf("%lld ",maxN); 32 } 33 return 0; 34 }
D.Destroy the Well of Life回到顶部
题意
n([1,250])口井,需要摧毁第n口井,wi,li,pi代表初始容量wi,最大容量li,摧毁花费pi(wi<=li, 0<=wi, li, pi <=20000)
当一口井wi>li,井自动摧毁
直接摧毁一口井,花费pi
当第i口井被摧毁,所有水自动加入i+1口井
题解
n只有250,直接模拟一下就行了,复杂度O(n^2)
PS:原来的题n=10^5,卡了挺久的,最后测了一下数据发现n只有250
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=250; 5 int w[N],l[N],p[N]; 6 int main(){ 7 int t,ca=1; 8 scanf("%d",&t); 9 while(t--){ 10 int n; 11 scanf("%d",&n); 12 for(int i=0;i<n;i++){ 13 scanf("%d%d%d",&w[i],&l[i],&p[i]); 14 } 15 int sum=p[n-1]; 16 for(int i=0;i<n;i++){ 17 int ans=0,ma=0; 18 for(int j=i;j<n;j++){ 19 ma+=w[j]; 20 if(ma<=l[j])ans+=p[j]; 21 } 22 sum=min(ans,sum); 23 } 24 printf("Case %d: Need to use %d mana points. ",ca++,sum); 25 } 26 return 0; 27 }
E.Just multiply it回到顶部
题意
求n([1,10^6])的所有因子乘积%10007
题解
枚举到根号n求出所有因子,需要注意n是不是完全平方数,复杂度O(sqrt(n))
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main(){ 5 int t; 6 scanf("%d",&t); 7 while(t--){ 8 int n; 9 scanf("%d",&n); 10 int b=sqrt(n+0.5); 11 int ans=1,p=10007; 12 if(n==b*b)ans=b,b--; 13 for(int i=2;i<=b;i++) 14 if(n%i==0) 15 ans=ans*1LL*i%p*(n/i)%p; 16 printf("%d ",ans); 17 } 18 return 0; 19 }
F.复习时间回到顶部
题意
复习后一门课的效率为前一门课之间的难度差的平方,而复习第一门课的效率为100和这门课的难度差的平方。xhd这学期选了n门课,但是一晚上他最多只能复习m门课,请问他复习单独一门课
的最高效率值是多少
题解
求出最小值,max(100-最小值的平方,最大值-最小值的平方),复杂度O(1)
PS:看不懂题意怎么办,猜啊
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main(){ 5 int t; 6 scanf("%d",&t); 7 while(t--){ 8 int n,m,x,minn=101,maxx=0; 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=n;i++){ 11 scanf("%d",&x); 12 minn=min(minn,x); 13 maxx=max(maxx,x); 14 } 15 printf("%d ",max((100-minn)*(100-minn),(maxx-minn)*(maxx-minn))); 16 } 17 return 0; 18 }
G.Barica回到顶部
题意
n个点k个方向([1,10^5]),初始在(x[1],y[1]),问按照k个方向跳完后,在哪个点,如果方向上没有点就不动,一个点跳过一次就会消失
当前在(x,y),A方向就是找一个最小的正整数P,存在一个点(x+P,y+P)
当前在(x,y),B方向就是找一个最小的正整数P,存在一个点(x+P,y-P)
当前在(x,y),C方向就是找一个最小的正整数P,存在一个点(x-P,y+P)
当前在(x,y),D方向就是找一个最小的正整数P,存在一个点(x-P,y-P)
题解
分析一下A,(x,y)->(x+P,y+P)我们做个坐标差可以得到,(y-x)=(y+P-x-P)=(y-x)那么只需要找到一个坐标差相同的并且x轴比x大的点即可
再分析一下B,(x,y)->(x+P,y-P)我们做个坐标和可以得到,(y+x)=(x+P+y-P)=(y+x)那么只需要找到一个坐标和相同的并且x轴比x大的点即可
同理C和D
先求一个坐标和,再求一个坐标差,按从小到大排序,如果相同则x小的在前面,复杂度O(nlogn)
PS:詹老哥的代码有点牛逼
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=1e5+5; 5 6 struct p{ 7 int x,y; 8 }a[N],b[N]; 9 int n,k; 10 char s[N]; 11 list<p>la,lb; 12 map<pair<int,int>,list<p>::iterator>map1,map2; 13 bool cmp1(p a,p b){ 14 return a.x+a.y<b.x+b.y||(a.x+a.y==b.x+b.y&&a.x<b.x); 15 } 16 bool cmp2(p a,p b){ 17 return a.x-a.y<b.x-b.y||(a.x-a.y==b.x-b.y&&a.x<b.x); 18 } 19 int main(){ 20 scanf("%d%d%s",&n,&k,s+1); 21 for(int i=1;i<=n;i++){ 22 scanf("%d%d",&a[i].x,&a[i].y); 23 b[i]=a[i]; 24 } 25 int sx=a[1].x,sy=a[1].y; 26 sort(a+1,a+1+n,cmp1); 27 sort(b+1,b+1+n,cmp2); 28 //puts(""); 29 //for(int i=1;i<=n;i++)printf("a(%4d,%4d) b(%4d,%4d) ",a[i].x,a[i].y,b[i].x,b[i].y); 30 //puts(""); 31 32 for(int i=1;i<=n;i++)la.push_back(a[i]); 33 for(int i=1;i<=n;i++)lb.push_back(b[i]); 34 35 list<p>::iterator q; 36 37 list<p>::iterator ita=la.begin(); 38 for(;ita!=la.end();ita++)if(ita->x==sx&&ita->y==sy)break; 39 for(q=la.begin();q!=la.end();q++)map1[{q->x,q->y}]=q; 40 41 list<p>::iterator itb=lb.begin(); 42 for(;itb!=lb.end();itb++)if(itb->x==sx&&itb->y==sy)break; 43 for(q=lb.begin();q!=lb.end();q++)map2[{q->x,q->y}]=q; 44 45 auto nowx=map1[{sx,sy}],nowy=map2[{sx,sy}]; 46 for(int i=1;i<=k;i++){ 47 if(s[i]==‘A‘){ 48 q=nowy; 49 if(q==lb.end())continue; 50 q++; 51 if(q->x>=nowy->x&&q->x-q->y==nowy->x-nowy->y){ 52 la.erase(map1[{nowy->x,nowy->y}]); 53 auto X=map1[{q->x,q->y}]; 54 nowx=X; 55 lb.erase(nowy); 56 nowy=q; 57 } 58 }else if(s[i]==‘B‘){ 59 q=nowx; 60 if(q==la.end())continue; 61 q++; 62 if(q->x>=nowx->x&&q->x+q->y==nowx->x+nowx->y){ 63 lb.erase(map2[{nowx->x,nowx->y}]); 64 auto X=map2[{q->x,q->y}]; 65 nowy=X; 66 la.erase(nowx); 67 nowx=q; 68 } 69 }else if(s[i]==‘C‘){ 70 q=nowx; 71 if(q==la.begin())continue; 72 q--; 73 if(q->x<=nowx->x&&q->x+q->y==nowx->x+nowx->y){ 74 lb.erase(map2[{nowx->x,nowx->y}]); 75 auto X=map2[{q->x,q->y}]; 76 nowy=X; 77 la.erase(nowx); 78 nowx=q; 79 } 80 }else if(s[i]==‘D‘){ 81 q=nowy; 82 if(q==lb.begin())continue; 83 q--; 84 if(q->x<=nowy->x&&q->x-q->y==nowy->x-nowy->y){ 85 la.erase(map1[{nowy->x,nowy->y}]); 86 auto X=map1[{q->x,q->y}]; 87 nowx=X; 88 lb.erase(nowy); 89 nowy=q; 90 } 91 } 92 //printf("a(%4d,%4d) b(%4d,%4d) ",nowx->x,nowx->y,nowy->x,nowy->y); 93 } 94 printf("%d %d ",nowx->x,nowx->y); 95 return 0; 96 }
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=1e5+5; struct P { int sum,x,id; bool operator <(const P &t)const { if(sum!=t.sum) return sum<t.sum; return x<t.x; } }a[N],b[N]; int x[N],y[N],f[4][N]; char s[N]; int main() { //freopen("in.txt","r",stdin); memset(f,-1,sizeof f); int n,k; scanf("%d%d%s",&n,&k,s); for(int i=1;i<=n;i++) { scanf("%d%d",&x[i],&y[i]); a[i]={x[i]-y[i],x[i],i},b[i]={x[i]+y[i],x[i],i}; } sort(a+1,a+n+1),sort(b+1,b+n+1); for(int i=2;i<=n;i++) { if(a[i].sum==a[i-1].sum) f[0][a[i-1].id]=a[i].id,f[3][a[i].id]=a[i-1].id; if(b[i].sum==b[i-1].sum) f[1][b[i-1].id]=b[i].id,f[2][b[i].id]=b[i-1].id; } int pos=1; for(int i=0;i<k;i++) { int t=s[i]-‘A‘; if(f[t][pos]==-1) continue; for(int j=0;j<4;j++) { if(f[j][pos]==-1) continue; f[3-j][f[j][pos]]=f[3-j][pos]; } pos=f[t][pos]; } printf("%d %d ",x[pos],y[pos]); return 0; }
H.Musical Themes回到顶部
题意
n([1,5000])个音符,求长度超过5的子串,需要满足,存在长度相同跟原子串不相交的新子串,使得新子串的每个数字等于对应旧子串的数字+常数P,数字范围1-88
题解
子串=新子串+P,如果对原子串进行差分,对于差分数组,子串=新子串
那么问题就变成求最长公共子串并且不相交,并且子串长度>=5
这个问题是后缀数组的经典题
求出sa和height,发现答案就在里面,所有height>=len的都可以,因为限制子串长度>=5
可以发现height是一段一段的递减,那么只需要满足每一段的max(sa)-min(sa)>=maxlen即可
maxlen我们可以二分得到,二分范围l=4,r=n/2,答案就是上面检查合法的ans再加上1,复杂度O(nlogn)
PS:sa的范围是[0,n],会多一个空串,一开始被[0,n)坑了
代码
1 #include<bits/stdc++.h> 2 #define cl(x) memset(x,0,sizeof(x)) 3 using namespace std; 4 const int N=1e5+5; 5 6 int n,rk[N],sa[N],height[N],tmp[N],cnt[N];int s[N]; 7 void suffixarray(int n,int m){ 8 int i,j,k;n++; 9 for(i=0;i<n*2+5;i++)rk[i]=sa[i]=height[i]=tmp[i]=0; 10 for(i=0;i<m;i++)cnt[i]=0; 11 for(i=0;i<n;i++)cnt[rk[i]=s[i]]++; 12 for(i=1;i<m;i++)cnt[i]+=cnt[i-1]; 13 for(i=0;i<n;i++)sa[--cnt[rk[i]]]=i; 14 for(k=1;k<=n;k<<=1){ 15 for(i=0;i<n;i++){ 16 j=sa[i]-k; 17 if(j<0)j+=n; 18 tmp[cnt[rk[j]]++]=j; 19 } 20 sa[tmp[cnt[0]=0]]=j=0; 21 for(i=1;i<n;i++){ 22 if(rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k])cnt[++j]=i; 23 sa[tmp[i]]=j; 24 } 25 memcpy(rk,sa,n*sizeof(int)); 26 memcpy(sa,tmp,n*sizeof(int)); 27 if(j>=n-1)break; 28 } 29 for(j=rk[height[i=k=0]=0];i<n-1;i++,k++) 30 while(~k&&s[i]!=s[sa[j-1]+k])height[j]=k--,j=rk[sa[j]+1]; 31 } 32 int a[N]; 33 bool check(int mid){ 34 int minn=n,maxx=0; 35 for(int i=2;i<=n;i++){ 36 if(height[i]>=mid) 37 minn=min(minn,min(sa[i],sa[i-1])), 38 maxx=max(maxx,max(sa[i],sa[i-1])); 39 //printf("i=%d h=%d %d %d ",i,height[i],minn,maxx); 40 if(height[i]<mid||i==n){ 41 if(minn+mid<maxx)return true; 42 minn=n,maxx=0; 43 } 44 } 45 return false; 46 } 47 int main() 48 { 49 scanf("%d",&n); 50 for(int i=0;i<n;i++)scanf("%d",&a[i]); 51 n--; 52 for(int i=0;i<n;i++)s[i]=a[i+1]-a[i]+100; 53 //for(int i=0;i<n;i++)printf("%4d ",s[i]); 54 suffixarray(n,200); 55 //puts(""); 56 //多一个空串 57 //for(int i=1;i<=n;i++)printf("%4d ",i);puts(""); 58 //for(int i=0;i<=n;i++)printf("%4d ",rk[i]);puts(""); 59 //for(int i=0;i<=n;i++)printf("%4d ",sa[i]);puts(""); 60 //for(int i=1;i<=n;i++)printf("%4d ",height[i]);puts(""); 61 int l=4,r=n/2,ans=-1; 62 while(l<=r){ 63 int mid=(l+r)>>1; 64 //printf("l=%d mid=%d r=%d ",l,mid,r); 65 if(check(mid))l=mid+1,ans=mid; 66 else r=mid-1; 67 } 68 if(ans<4)printf("0"); 69 else printf("%d",ans+1); 70 return 0; 71 } 72 /* 73 10 74 1 2 3 4 5 6 7 8 9 11 75 */
I.突破包围回到顶部
题意
n*m([2,100])的图,*代表草丛,草丛数量不超过100,#代表墙,需要从y->d,但是敌人每隔a[k]秒会切回监控,yzq就需要躲在草丛里来躲避监控,问能否从y->d
题解
因为最多只有100个草丛+起点+终点,一共102个点,可以求出两两之间的最小距离
知道时间a[k],从起点出发,遍历其余101个点,如果距离<=a[k],那么标记这个点可以到,以此类推,复杂度O(n*m*102)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int N=105; 5 int leafx[N],leafy[N]; 6 char G[N][N]; 7 bool ok[N],changeok[N]; 8 int v[N][N][N]; 9 int dx[]={0,0,1,-1}; 10 int dy[]={1,-1,0,0}; 11 int sx,sy,ex,ey,n,m; 12 void bfs(int leaf,int beginx,int beginy){ 13 queue<int>qx,qy; 14 qx.push(beginx);qy.push(beginy); 15 v[leaf][beginx][beginy]=0; 16 while(!qx.empty()){ 17 int ux=qx.front(),uy=qy.front(); 18 qx.pop();qy.pop(); 19 for(int i=0;i<4;i++){ 20 int vx=ux+dx[i],vy=uy+dy[i]; 21 if(vx>=1&&vx<=n&&vy>=1&&vy<=m&&G[vx][vy]==‘.‘&&v[leaf][vx][vy]>v[leaf][ux][uy]+1){ 22 v[leaf][vx][vy]=v[leaf][ux][uy]+1; 23 qx.push(vx);qy.push(vy); 24 } 25 } 26 } 27 } 28 int main(){ 29 int t; 30 scanf("%d",&t); 31 while(t--){ 32 int cnt=0; 33 scanf("%d%d",&n,&m); 34 for(int i=1;i<=n;i++)scanf("%s",G[i]+1); 35 for(int i=1;i<=n;i++) 36 for(int j=1;j<=m;j++){ 37 if(G[i][j]==‘y‘)sx=i,sy=j,G[i][j]=‘.‘; 38 else if(G[i][j]==‘d‘)ex=i,ey=j,G[i][j]=‘.‘; 39 else if(G[i][j]==‘*‘)leafx[cnt]=i,leafy[cnt++]=j,G[i][j]=‘.‘; 40 } 41 memset(v,0x3f,sizeof v); 42 for(int i=0;i<cnt;i++) 43 bfs(i,leafx[i],leafy[i]); 44 bfs(cnt,sx,sy);leafx[cnt]=sx;leafy[cnt]=sy;cnt++;//begin 45 bfs(cnt,ex,ey);leafx[cnt]=ex;leafy[cnt]=ey;cnt++;//end 46 int k,ti; 47 scanf("%d",&k); 48 memset(ok,false,sizeof ok); 49 ok[cnt-2]=1;//begin 50 for(int i=1;i<=k;i++){ 51 scanf("%d",&ti); 52 if(ok[cnt-1])continue; 53 for(int j=0;j<cnt;j++)changeok[j]=ok[j]; 54 for(int j=0;j<cnt;j++){ 55 if(!ok[j])continue; 56 for(int l=0;l<cnt;l++){ 57 if(ok[l])continue; 58 if(v[j][leafx[l]][leafy[l]]<=ti){ 59 //printf("(%d,%d) dis=%d ti=%d i=%d ",j,l,v[j][leafx[l]][leafy[l]],ti,i); 60 changeok[l]=true; 61 } 62 } 63 } 64 for(int j=0;j<cnt;j++)ok[j]=changeok[j]; 65 } 66 printf("%s ",ok[cnt-1]?"good luck!":"poor yzq!"); 67 } 68 return 0; 69 }
J.Trojke回到顶部
题意
n*n([1,100])的图,上面有最多26个不同的大写字母,问三点共线的数量
题解
暴力枚举,复杂度O(26^3)
PS:题意有点误导
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int x[27],y[27],tot; 5 char G[105][105]; 6 int main(){ 7 int n; 8 scanf("%d",&n); 9 for(int i=1;i<=n;i++)scanf("%s",G[i]+1); 10 for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(G[i][j]!=‘.‘)x[tot]=i,y[tot++]=j; 11 int ok=0; 12 for(int i=0;i<tot;i++) 13 for(int j=i+1;j<tot;j++) 14 for(int k=j+1;k<tot;k++) 15 if((y[j]-y[i])*(x[k]-x[i])==(x[j]-x[i])*(y[k]-y[i])) 16 ok++; 17 printf("%d",ok); 18 return 0; 19 }
以上是关于TZOJ 挑战题库随机训练04的主要内容,如果未能解决你的问题,请参考以下文章