木有闲话qwq
A.[SDOI2016]齿轮
看到名字,发现是2016省选题,于是先放着打后面去了,然而,后来发现贼简单???
只需要判断下各个边给出的约束条件是否有矛盾就行。我们可以考虑下稍微简化的版本:
给出m个约束条件,每个条件是u比v小x,求是否有矛盾。
对于这个题,我们可以直接令一个点为0,然后,根据这个点和其他点的关系推出其他点的值就行了,中途判断是否矛盾即可。
而这个题,仅仅是给的条件变成了u是v的x倍而已。
于是,我们另一个点为1(因为要转动,不能是0嘛),然后跟上面一样的做法即可
不过,有可能有些点不在同一个连通块,对于不同连通块我们分别判断即可~
#include<bits/stdc++.h> using namespace std; const int N=1001,M=1e4+1; const double eps=1e-3; struct node{ int v,x,y,nex; }t[M<<1]; int len,las[N]; double dis[N]; bool vis[N]; inline void add(int u,int v,int x,int y){ t[++len]=(node){v,x,y,las[u]},las[u]=len; } inline bool check(double x,double y){ return fabs(x-y)<=eps; } inline bool dfs(int x){ vis[x]=1; for(int i=las[x];i;i=t[i].nex){ int v=t[i].v;double a=t[i].x,b=t[i].y; double res=(dis[x]/a)*b; if(!vis[v]){ dis[v]=res; if(!dfs(v)){ return false; } }else{ if(!check(res,dis[v])){ return false; } } } return true; } int main(){ int T; scanf("%d",&T); for(int ti=1;ti<=T;++ti){ int n,m;memset(las,0,sizeof(las)),len=0; memset(vis,0,sizeof(vis)); scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int u,v,x,y; scanf("%d%d%d%d",&u,&v,&x,&y); add(u,v,x,y),add(v,u,y,x); } bool flag=1; for(int i=1;i<=n;++i){ if(!vis[i]){ dis[i]=1; if(!dfs(i)){ flag=0; break; } } } printf("Case #%d: ",ti); if(!flag){ puts("No"); }else{ puts("Yes"); } } return 0; }
B.Rinne Loves Xor
发现前面一节其实就是个常数,我们先不管它,我们着重看后面那个求和
因为后面两个是个等价的问题,我们只考虑其中一个怎么做
其实就是等价于求:
(两个问题是等价的,只是表示方法不同罢了)
对于这个问题,第一反应是搞个前缀和,然而,异或之和并不等于和的异或,所以,是错的
这样的话,我们就可以考虑另一个套路——按位计算
我们计算每一位的贡献即可。
对于二进制的第k位,如果的第k位为0,那么,对于前面所有的第k位为1的,都会给答案提供一个的贡献,而这个是可加的,同理,如果的第k位为1,我们只需统计第k为0的个数即可,计算完贡献后再统计一下就可以了
会算这个后,我们就可以等价的计算题目的式子了~
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e5+1,mod=1e9+7; int a[N],b[N],c[N]; int A[30][2],B[30][2]; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); } for(int i=1;i<=n;++i){ scanf("%d",&b[i]); } c[0]=0; for(int i=1;i<=n;++i){ c[i]=(c[i-1]+(a[i]^b[i]))%mod; for(int j=29;~j;--j){ c[i]=(c[i]+(1LL*B[j][((a[i]>>j)&1)^1]*(1<<j))%mod)%mod; c[i]=(c[i]+(1LL*A[j][((b[i]>>j)&1)^1]*(1<<j))%mod)%mod; ++A[j][((a[i]>>j)&1)];++B[j][((b[i]>>j)&1)]; } printf("%d ",c[i]); } return 0; }
C.阶乘
这题有两种解法,不过都需要对p进行质因数分解
对于质因数分解,一般我们有两种做法:
1.
2.
当然还有其他很多的比如Pollard Rho,但这并不在我们讨论之中
考虑到数据范围
如果,我们使用预处理的话,则会超时,所以,我们考虑使用第2种也是最简朴的那种算法——枚举质因子
由于太简单了,就不再说明,不会的可以看代码qwq
我们假设分解完后(这里使用的是一般的表示方法,指的一个质数和前面的p不同)
那么,我们对于每一组的我们计算出最小的一个Ki使得Ki的阶乘是的倍数,答案取最大的即可(因为每组质因数之间没有关联)
那么,现在就是如何求这个了。
方法一——二分
注意到,明显满足二分性,那么,我们只需要求出二分的一个数字x的阶乘可以分解为多少个相乘即可,结论是:
这里就不再推导,不清楚的童鞋可以自行推导下,挺简单的
然后,我们算出了有多少个后,和比较下大小即可
方法二——暴力枚举
因为对的个数有影响的只有的倍数,所以我们可以暴力枚举的倍数,直到枚举出的的所有倍数分解出的的个数大于等于即可
但是,复杂度为啥是对的?
我们分析下:
因为是指数,而至少为2,所以,的大小是log级别的
所以,我们每次最多枚举log个倍数即可算出答案了
代码:
方法一:
#include<bits/stdc++.h> using namespace std; const int N=1e5+1; int sav[N],tot[N],e; inline bool check(int x){ for(int i=1;i<=e;++i){ int y=x,res=0; while(y){ res+=(y/sav[i]); y/=sav[i]; } if(res<tot[i]){ return false; } } return true; } int main(){ int T; scanf("%d",&T); while(T--){ int p;e=0; scanf("%d",&p); for(int i=2;i<=sqrt(p);++i){ if(p%i==0){ sav[++e]=i;tot[e]=0; while(p%i==0){ ++tot[e];p/=i; } } } if(p>1){ sav[++e]=p;tot[e]=1; } int l=1,r=1e9,ans=0; while(l<=r){ int mid=(l+r)>>1; if(check(mid)){ r=mid-1;ans=mid; }else{ l=mid+1; } } printf("%d\n",ans); } return 0; }
方法二:
#include<bits/stdc++.h> using namespace std; const int N=1e5+1; int sav[N],tot[N],e; int main(){ int T; scanf("%d",&T); while(T--){ int p;e=0; scanf("%d",&p); for(int i=2;i<=sqrt(p);++i){ if(p%i==0){ sav[++e]=i;tot[e]=0; while(p%i==0){ ++tot[e];p/=i; } } } if(p>1){ sav[++e]=p;tot[e]=1; } int ans=1; for(int i=1;i<=e;++i){ int rep=0; for(int j=sav[i];;j+=sav[i]){ int now=j; while(now%sav[i]==0){ ++rep;now/=sav[i]; } if(rep>=tot[i]){ ans=max(ans,j); break; } } } printf("%d\n",ans); } return 0; }
D.小石的签到题
咋看之下不可做,写个暴力规律过。。。
找规律发现只有n=1时Yang才会胜利,然后。。。就没然后了
#include<bits/stdc++.h> using namespace std; int main(){ int n; scanf("%d",&n); if(n==1){ puts("Yang"); }else{ puts("Shi"); } return 0; }
E.装备合成
显然满足三分(感觉/x),于是直接打个三分枚举用第一个方法造几次装备即可
(左端点记得为0,我因为这个挂了好久qwq)
#include<bits/stdc++.h> using namespace std; #define int long long int n,m; inline int calc(int x){ int L=n-x*2,R=m-x*3; return x+min(L/4,R); } signed main(){ int T; scanf("%lld",&T); while(T--){ scanf("%lld%lld",&n,&m); int l=0,r=min(n/2,m/3),ans=0; while(l<=r){ int lc=l+(r-l)/3,rc=r-(r-l)/3; int res1=calc(lc),res2=calc(rc); ans=max(ans,max(res1,res2)); if(res2<res1){ r=rc-1; }else{ l=lc+1; } } printf("%lld\n",ans); } return 0; }