CF#511 Div2
A.Little C Loves 3 I
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n,a,b,c; 5 int main() { 6 scanf("%d",&n); 7 if (n%3==0) a=b=1; 8 else if (n%3==1) a=b=1; 9 else a=1,b=2; 10 c=n-a-b; 11 printf("%d %d %d\n",a,b,c); 12 return 0; 13 }
B.Cover Points
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 int n,ans; 5 int main() { 6 scanf("%d",&n); 7 for (int x,y,i=1;i<=n;i++) { 8 scanf("%d%d",&x,&y); 9 ans=max(ans,x+y); 10 } 11 printf("%d\n",ans); 12 return 0; 13 }
C.Enlarge GCD
题意:去掉最少的数使得$gcd$变大
做法:先将所有数除一个$gcd$,这样只需要知道含有某个质因子的数有多少个,将剩下的去掉即为一组可行解。不过可以预处理每个数的最小质因子,这样将分解质因数的时间代价降为$O(log_{2}n)$
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 const int N=300010; 6 const int S=15000001; 7 int n,cnt; int a[N]; 8 int pri[S],mi[S],sum[S]; bool mark[S]; 9 int gcd(int x,int y) { 10 if (!y) return x; 11 return gcd(y,x%y); 12 } 13 void pre() { 14 for (int i=2;i<S;i++) { 15 if (!mark[i]) 16 mi[i]=++cnt,pri[cnt]=i; 17 for (int j=1;j<=cnt;j++) { 18 if (i*pri[j]>=S) break; 19 mark[i*pri[j]]=1; 20 mi[i*pri[j]]=j; 21 if (i%pri[j]==0) break; 22 } 23 } 24 return; 25 } 26 void read() { 27 scanf("%d",&n); 28 for (int i=1;i<=n;i++) 29 scanf("%d",&a[i]); 30 int t=0; 31 for (int i=1;i<=n;i++) 32 t=gcd(t,a[i]); 33 for (int i=1;i<=n;i++) 34 a[i]/=t; 35 return; 36 } 37 bool judge() { 38 for (int i=1;i<=n;i++) 39 if (a[i]!=1) 40 return false; 41 return true; 42 } 43 void work() { 44 if (judge()) { 45 printf("-1\n"); 46 return; 47 } 48 for (int i=1;i<=n;i++) { 49 int last=0; 50 while(a[i]!=1) { 51 int t=mi[a[i]]; 52 if (t!=last) 53 last=t,sum[t]++; 54 a[i]/=pri[t]; 55 } 56 } 57 int ans=n-1; 58 for (int i=1;i<=cnt;i++) 59 ans=min(ans,n-sum[i]); 60 printf("%d\n",ans); 61 return; 62 } 63 int main() { 64 pre(); 65 read(); 66 work(); 67 return 0; 68 }
D.Little C Loves 3 II
题意:棋盘上曼哈顿距离为$3$的位置可以俩俩匹配,问最多多少个格子匹配
做法:比赛的时候先写了一个匈牙利算法跑暴力,调了很长时间,然后试了一会找到规律
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 typedef long long ll; 5 ll n,m; 6 int main() { 7 scanf("%I64d%I64d",&n,&m); 8 if (n>m) swap(n,m); 9 if (n==1) { 10 ll t1=m/6,t2=max(0ll,m%6-3); 11 printf("%I64d\n",t1*6+t2*2); 12 } 13 else if (n==2) { 14 if (m==2) printf("0\n"); 15 else if (m==3) printf("4\n"); 16 else if (m==7) printf("12\n"); 17 else printf("%I64d\n",n*m); 18 } 19 else { 20 if (n&1) swap(n,m); 21 if (n&1) printf("%I64d\n",n*m-1); 22 else printf("%I64d\n",n*m); 23 } 24 return 0; 25 }
E.Region Separation
题意:给出一棵树,点有点权,要划分为若干个级别,每个级别都将整棵树分为若干个点权和相等的连通块,要求每个级别的连通块都是对上一级别的连通块内划分,求方案数
题意比较难懂,给个样例解释一下
 
$ans=3$
$Plan A: level1\ \left\{1,2,3,4\right\}$
$Plan B: level1\ \left\{1,2,3,4\right\}\ level2\ \left\{1,2\right\},\left\{3,4\right\}$
$Plan C:level1\ \left\{1,2,3,4\right\},\ levle2\ \left\{1,3\right\},\left\{2\right\},\left\{4\right\}$
做法:考虑将整棵树分为$k$个区域,定义用$s_{i}$表示第$i$个点的子树权值和,$1$为根
那么一定存在$k$个点满足$s_{i} \equiv 0 mod \frac{s_{1}}{k}$,那么若点$i$满足$k$划分,那么一定满足
$k=\frac{x*s_{1}}{s_{i}}, x$为正整数,所以$k$中含有因子$\frac{s_{1}}{s_{1}/s_{n}}$,即这个点可能对该划分有贡献
那么统计每个点都可能对哪些数量的划分有贡献,再考虑统计方案数
如果可以划分为$k$块,那么按照$plan$的区分方式,会对$k*x$块的方案有贡献,因此累加俩个dp数组
$f[i]$表示有多少点可以对$i$划分有贡献
$g[i]$表示$i$划分有多少方案数
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; const int N=1000010; const int mod=1000000007; int n,ans; int a[N],p[N],f[N],g[N]; ll s[N]; ll gcd(ll x,ll y) { if (!y) return x; return gcd(y,x%y); } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=a[i]; for (int i=2;i<=n;i++) scanf("%d",&p[i]); for (int i=n;i>1;i--) s[p[i]]+=s[i]; for (int i=1;i<=n;i++) { ll t=s[1]/gcd(s[1],s[i]); if (t<=n) f[t]++; } for (int i=n;i>=1;i--) for (int j=i*2;j<=n;j+=i) f[j]+=f[i]; g[1]=1; for (int i=1;i<=n;i++) if (f[i]==i) { (ans+=g[i])%=mod; for (int j=2*i;j<=n;j+=i) (g[j]+=g[i])%=mod; } printf("%d\n",ans); return 0; }
                    
                
                
            
        
浙公网安备 33010602011771号