111 / 423 Problem A LightOJ 1370 Bi-shoe and Phi-shoe
d.给出n个数a1,a2,...,an,对每个数ai分别找到一个相应的数pi,使其欧拉函数值Φ(pi)>=ai,求p1+p2+...+pn的最小值。
s.不知道为什么pi要选大于ai的第一个素数。
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> using namespace std; const int MAXN=1000100; bool isPrime[MAXN]; void sieve(int n){ memset(isPrime,true,sizeof(isPrime)); isPrime[0]=isPrime[1]=false; int i,j,k; k=sqrt(n); for(i=2;i<=k;++i){ if(isPrime[i]==true){ for(j=i+i;j<=n;j=j+i){ isPrime[j]=false; } } } } int main(){ sieve(MAXN-1); int T; int n; int t; int i; int j; long long sum; int ca=0; scanf("%d",&T); while(T--){ scanf("%d",&n); sum=0; for(i=0;i<n;++i){ scanf("%d",&t); for(j=t+1;j<MAXN;++j){ if(isPrime[j]==true){ break; } } sum+=j; } printf("Case %d: %lld Xukha\n",++ca,sum); } return 0; }
21 / 74 Problem B LightOJ 1356 Prime Independence
题意:
找出一些数字的最大质独立集,就是集合能的所有数互相之间不会出现 a[i]==t*a[j] (t是质数) 的情况。
思路:
首先想最大独立集对于一般图是NP问题,通常只有求二分图最大独立集,然后就是如何把这些数字分为二分图。
能够想到如果一个数字等于另一个数字乘以一个质数,那么这两个数字的质因子分解应该只有这一个质数的差别。也就是只会多一个质数,数字上限只有500000,完全可以看一个数组储存某个数字是否存在,然后对每个数字分解质因子,找到他对于每个质因子能够找到的那个数,存在则加边,然后就把质因子总个数(可重复的)是奇数与偶数的分成两边并且把只有一个质因子差别的连通,然后二分图匹配求最大独立集即可。
即二分图左边为质因子总数为奇数的,右边为质因子总数偶数的。
关于二分图匹配因为数量较大所以用匈牙利会tle,要用Hopcroft-Carp:
c.根据这个思路写了下,用的匈牙利O(VE),没超时。不过Hopcroft-Carp这个会快1s左右。
/* //顶点编号从1开始的 用STL中的vector建立邻接表实现匈牙利算法 效率比较高 处理点比较多的效率很高。1500的点都没有问题 */ #include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<math.h> using namespace std; const int MAXN=40000+10;//这个值要超过两边个数的较大者,因为有linker int linker[MAXN]; bool used[MAXN]; vector<int>G[MAXN]; int uN; bool dfs(int u) { int sz=G[u].size(); for(int i=0; i<sz; i++) { if(!used[G[u][i]]) { used[G[u][i]]=true; if(linker[G[u][i]]==-1||dfs(linker[G[u][i]])) { linker[G[u][i]]=u; return true; } } } return false; } int hungary() { int u; int res=0; memset(linker,-1,sizeof(linker)); for(u=0; u<uN; u++) { memset(used,false,sizeof(used)); if(dfs(u)) res++; } return res; } const int MAXN2=500000+10; bool exist[MAXN2];//标记数是否存在 int a[MAXN];//原来的数 int myHash[MAXN2];//离散化 int factors[MAXN2][2];//[0]存质因子,[1]存个数 int factCnt;//不同的质因子总个数 int factCnt2;//包含相同的质因子的总个数 void getFactors(int n){ int i,k; factCnt=0; factCnt2=0; for(i=2,k=sqrt(n);i<=k;++i){ if(n%i==0){ factors[factCnt][0]=i; factors[factCnt][1]=1; ++factCnt2; n=n/i; while(n%i==0){ ++factors[factCnt][1]; ++factCnt2; n=n/i; } ++factCnt; k=sqrt(n);//循环条件不直接写i<=sqrt(n);是因为这样可以避免重复开跟方 } } if(n>1){ factors[factCnt][0]=n; factors[factCnt][1]=1; ++factCnt; ++factCnt2; } } int main(){ int T; int N; int i; int j; int tmp; int ans; int ca=0; scanf("%d",&T); while(T--){ scanf("%d",&N); uN=N;//左边集合个数 for(i=0;i<uN;++i){//记得要清空 G[i].clear(); } memset(exist,false,sizeof(exist)); for(i=0;i<N;++i){ scanf("%d",&a[i]); exist[a[i]]=true; myHash[a[i]]=i;//离散化,二分图N个点就可以了 } for(i=0;i<N;++i){ getFactors(a[i]);//获取质因子 for(j=0;j<factCnt;++j){//枚举质因子 tmp=a[i]/factors[j][0]; if(exist[tmp]==true){//tmp存在 if(factCnt2&1){//第i个数质因数个数为奇数,第i个数作为左边的点插入一条由左指向右的有向边 G[i].push_back(myHash[tmp]); } else{ G[myHash[tmp]].push_back(i); } } } } ans=hungary(); printf("Case %d: %d\n",++ca,N-ans); } return 0; }
c2.Hopcroft-Carp,这个比较快,O(squt(n)*E)
/* //顶点编号从0开始的 二分图匹配(Hopcroft-Karp算法) 复杂度O(squt(n)*E) 邻接表存图,vector实现 vector先初始化,然后加入边 uN为左端的顶点数,使用前赋值(点编号0开始) */ #include<iostream> #include<stdio.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; const int MAXN=40000+10; const int INF=0x3f3f3f3f; vector<int>G[MAXN]; int uN; int Mx[MAXN],My[MAXN]; int dx[MAXN],dy[MAXN]; int dis; bool used[MAXN]; bool SearchP(){ queue<int>Q; dis=INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i=0;i<uN;i++) if(Mx[i]==-1){ Q.push(i); dx[i]=0; } while(!Q.empty()){ int u=Q.front(); Q.pop(); if(dx[u]>dis)break; int sz=G[u].size(); for(int i=0;i<sz;i++){ int v=G[u][i]; if(dy[v]==-1){ dy[v]=dx[u]+1; if(My[v]==-1)dis=dy[v]; else{ dx[My[v]]=dy[v]+1; Q.push(My[v]); } } } } return dis!=INF; } bool DFS(int u){ int sz=G[u].size(); for(int i=0;i<sz;i++){ int v=G[u][i]; if(!used[v]&&dy[v]==dx[u]+1){ used[v]=true; if(My[v]!=-1&&dy[v]==dis)continue; if(My[v]==-1||DFS(My[v])){ My[v]=u; Mx[u]=v; return true; } } } return false; } int MaxMatch(){ int res=0; memset(Mx,-1,sizeof(Mx)); memset(My,-1,sizeof(My)); while(SearchP()){ memset(used,false,sizeof(used)); for(int i=0;i<uN;i++) if(Mx[i]==-1&&DFS(i)) res++; } return res; } const int MAXN2=500000+10; bool exist[MAXN2];//标记数是否存在 int a[MAXN];//原来的数 int myHash[MAXN2];//离散化 int factors[MAXN2][2];//[0]存质因子,[1]存个数 int factCnt;//不同的质因子总个数 int factCnt2;//包含相同的质因子的总个数 void getFactors(int n){ int i,k; factCnt=0; factCnt2=0; for(i=2,k=sqrt(n);i<=k;++i){ if(n%i==0){ factors[factCnt][0]=i; factors[factCnt][1]=1; ++factCnt2; n=n/i; while(n%i==0){ ++factors[factCnt][1]; ++factCnt2; n=n/i; } ++factCnt; k=sqrt(n);//循环条件不直接写i<=sqrt(n);是因为这样可以避免重复开跟方 } } if(n>1){ factors[factCnt][0]=n; factors[factCnt][1]=1; ++factCnt; ++factCnt2; } } int main(){ int T; int N; int i; int j; int tmp; int ans; int ca=0; scanf("%d",&T); while(T--){ scanf("%d",&N); uN=N;//左边集合个数 for(i=0;i<uN;++i){//记得要清空 G[i].clear(); } memset(exist,false,sizeof(exist)); for(i=0;i<N;++i){ scanf("%d",&a[i]); exist[a[i]]=true; myHash[a[i]]=i;//离散化,二分图N个点就可以了 } for(i=0;i<N;++i){ getFactors(a[i]);//获取质因子 for(j=0;j<factCnt;++j){//枚举质因子 tmp=a[i]/factors[j][0]; if(exist[tmp]==true){//tmp存在 if(factCnt2&1){//第i个数质因数个数为奇数,第i个数作为左边的点插入一条由左指向右的有向边 G[i].push_back(myHash[tmp]); } else{ G[myHash[tmp]].push_back(i); } } } } ans=MaxMatch(); printf("Case %d: %d\n",++ca,N-ans); } return 0; }
61 / 332 Problem C LightOJ 1341 Aladdin and the Flying Carpet
题意:给个矩形的面积a,和矩形的最小边长b,问有多少种矩形的方案(不能是正方形)
分析:a可以写成x,y,因为不能是正方形,所以设x<y,那么x<sqrt(a),y>sqrt(a)
所以找到所有小于sqrt(a)的因子,看有几个大于等于b的就是方案数
因子可以由数的唯一分解定理,求得
具体 : 先筛一遍1e6以内的素数,有线性筛,然后分解a,然后dfs找所有的小于sqrt(a)的因子,
由于前12个素数的乘积大于1e12了,所以这部分复杂度,大概是O(2^12)(一般还要略大,不过大不了多少,数组要开大)左右
可以用这个估计(因为是求小于sqrt(a)的,可以除以2,当然这是空间常数)
所以这部分复杂度是O(T*2^12)满的话(4000*4000)大概也就是几百万,这部分可以忽略不计
主要的复杂度在分解素数里,因为1e6里面大概有7w多素数,这部分复杂度(最坏的情况a是大素数),大概是4000*70000,可以卡过,由于不可能都是这种数据
所以还是可以过的
吐槽:然后我看了看网上的代码,都是先求出总的,然后暴力扫b减,结果居然过了,b是sqrt(a)的级别,是百万,4000*1e6,是4e9,TLE
出题人太良心,没有卡这种的QAQ,感觉略坑啊
#include <cstdio> #include <iostream> #include <ctime> #include <vector> #include <cmath> #include <map> #include <queue> #include <algorithm> #include <cstring> using namespace std; typedef long long LL; const int N=1e6+5; const int INF=0x3f3f3f3f; int cnt; bool v[N]; LL prime[80000]; void getprime(){ for(int i=2;i*i<=N-5;++i) if(!v[i]) for(int j=i*i;j<=N-5;j+=i) v[j]=1; for(int i=2;i<=N-5;++i) if(!v[i])prime[++cnt]=i; } vector<LL>fac[2]; int divisors[5000],tot; LL k; void dfs(int pos,LL res){ if(pos==fac[0].size()){ divisors[++tot]=res; return; } for(LL i=0,now=1;i<=fac[1][pos];now*=fac[0][pos],++i){ if(now*res>=k)break; dfs(pos+1,res*now); } } int main() { getprime(); int cas=0,T; scanf("%d",&T); while(T--){ printf("Case %d: ",++cas); LL a,b; scanf("%lld%lld",&a,&b); k=sqrt(a); if(k*k!=a)++k; if(b>=k){ printf("0\n"); continue; } LL t=a; fac[0].clear(),fac[1].clear(); for(int i=1;i<=cnt&&prime[i]*prime[i]<=t;++i){ if(t%prime[i])continue; int tmp=0; fac[0].push_back(prime[i]); while(t%prime[i]==0)++tmp,t/=prime[i]; fac[1].push_back(tmp); } if(t>1){ fac[0].push_back(t); fac[1].push_back(1); } tot=0; dfs(0,1); int ans=0; for(int i=1;i<=tot;++i) if(divisors[i]>=b)++ans; printf("%d\n",ans); } return 0; }