突然想起了写一下博客,主要是针对昨天模拟训练一的一些题的补题,但是感觉这些题中间有一些思想是少了题解就想不出来的。。看来还是得多加练习。
题意:有m块石头围成一圈,分别标号为1~m-1,另外有n只青蛙,每只青蛙每次只能跳a[i]格,问这所有的青蛙所能到达石头上面的编号之和。
题解:这个题的话是容斥原理的,每只青蛙能够到达的石块一定是gcd(a[i],m)的倍数,所以就转化成为了容斥原理,但是dfs的容斥原理时间复杂度是O(n^2),会超时,所以这样是不妥的。在网上众多题解的帮助下,我知道了其实如果考虑m的所有因子所做的贡献可以将时间复杂度降低到O((logm)^2)(m的因子平均大概为logm个)。具体得先将这些因子从小到大排序,并定义vis数组标记m的因子是否能被gcd(a[i],m)整除,num数组用于记录某个因子在前面的运算中被运算几次再后来的计算时可知这个因子是容还是斥。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> using namespace std; int gcd(int x,int y){ return y==0 ? x : gcd(y,x%y); } int n,m,a[100005],p[100005],num[100005],vis[100005]; int main(){ int t; scanf("%d",&t); int T=t; while(t--){ long long ans=0; int cnt=0; scanf("%d%d",&n,&m); for(int i=1;i*i<=m;i++){ if(m%i==0){ cnt++; p[cnt]=i; if(i*i!=m){ cnt++; p[cnt]=m/i; } } } memset(vis,0,sizeof(vis)); memset(num,0,sizeof(num)); sort(p+1,p+cnt+1); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); a[i]=gcd(a[i],m); for(int j=1;j<cnt;j++){ if(p[j]%a[i]==0) vis[j]=1; } } for(int i=1;i<=cnt;i++){ //printf("%d %d ",vis[i],num[i]); if(vis[i]!=num[i]){ int t=(m-1)/p[i]; int u=vis[i]-num[i]; ans+=(long long)t*(t+1)/2*u*p[i]; for(int j=i;j<=cnt;j++){ if(p[j]%p[i]==0) num[j]+=u; } } } printf("Case #%d: %I64d ",T-t,ans); } return 0; }
题意:有一群人在排队,排了很久过后大家的队伍乱了,忘了自己排在哪了,只记的自己的身高和有多少个人站在自己前面或者后面,要求输出原来排队的顺序,如果不可能就输出"impossible"。
题解:这题是一道线段树的题,然而在拥有题解之前我根本没看出来。。。这题把节点设置为区间内剩余的空位。先将数据按照身高从小到大排,从小到大一次更新,每次先计算这个人应该放在第p个空位,然后再线段树下将如果p比左子节点大就将(p-左子节点)往右走,否则p不变往左走,到叶子节点时记录结果并更新值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> using namespace std; int tre[400005],ans[100005]; struct node{ int h,p; } a[100005]; bool cmp(node x,node y){ if(x.h==y.h) return x.p<y.p; return x.h<y.h; } void build(int num,int l,int r){ if(l==r){ tre[num]=1; return; } int mid=(l+r)/2; build(num*2,l,mid); build(num*2+1,mid+1,r); tre[num]=tre[num*2]+tre[num*2+1]; } void update(int num,int l,int r,int w,int k){ if(l==r){ tre[num]=0; ans[l]=k; return; } int mid=(l+r)/2; if(w<=tre[num*2]) update(num*2,l,mid,w,k); else update(num*2+1,mid+1,r,w-tre[num*2],k); tre[num]=tre[num*2]+tre[num*2+1]; } int main(){ int t; scanf("%d",&t); int T=t; while(t--){ int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&a[i].h,&a[i].p); sort(a+1,a+1+n,cmp); int flag=0; build(1,1,n); for(int i=1;i<=n;i++){ if(a[i].p>=n-i+1){ flag=1; break; } int t=min(a[i].p+1,n-i+1-a[i].p); update(1,1,n,t,a[i].h); } if(flag){ printf("Case #%d: impossible ",T-t); continue; } printf("Case #%d: ",T-t); for(int i=1;i<n;i++){ printf("%d ",ans[i]); } printf("%d ",ans[n]); } return 0; }