A题:
注意:The carpet was rectangular shaped, but not square shaped.
题意:求a的所有因数对中大于等于b的因数对。即给两个数a,b,求满足c*d==a且c>=b且d>=b的c,d二元组对数,(c,d)和(d,c)属于同一种情况
思路:根据唯一分解定理,先将a唯一分解,则a的所有正约数的个数为num = (1 + a1) * (1 + a2) *...(1 + ai),这里的ai是素因子的指数,因为题目说了不会存在c==d的情况,因此num可以直接除以2(就是多少对因数对),然后枚举小于b的a的约数,拿num减掉就可以了
下面是关于唯一分解定理的知识:
唯一分解定理:任何>1的整数都可以分解成有限个质数的乘积即n = P1^a1 * P2^a2 * …………*Pn^an (P1 < P2 < ……Pn);其中P为质因数,a为质因数的指数。
即:每个大于1的自然数均可写为质数的积,而且这些素因子按大小排列之后,写法仅有一种方式。(唯一)
算术基本定理:任何大于1的正整数都能为唯一分解为有限个质数的乘积因为一个数肯定是由合数和质数构成的,合数又可以分解成质数和合数,最后递归下去就会变成质数的乘积
存在性证明
首先n是大于1的,所以n只可能是质数或者合数,然而如果为质数,这显然是不可能的,因为在这种条件下,n可以表示成n^1 的形式,符合唯一分解定理,所以n只能是合数,所以n一定可以表示成两个数相乘(合数定义) 令 n = a * b ,因为n是假设条件下最小的数,所以a可以表示成两个质数的乘积 a=x*y, b同理表示成b=l*r,显然x,y,l,r均为质数,所以n可以表示成n=x*y*l*r,也就是说假设不成立,所以大于1的自然数必可写成质数的乘积。
唯一性证明
M = P1 * P2 * … * Pr = Q1 * Q2 * … * Qs
不妨设P1 <= P2 <= … <= Pr, Q1 <= Q2 <= … <= Qs。显然,P1是不等于Q1的,不然两边同时约掉它,
我们就得到一个更小的有两种分解方法的数。不妨设P1 < Q1,那么我们用P1替换掉等式最右边中的Q1,
得到一个比M更小的数T = P1 * Q2 * Q3 * … * Qs。令M’ = M – T,我们得到M’的两种表达:
M’ = (P1 * P2 * … * Pr) – (P1 * Q2 * … * Qs) = P1 * (P2 * .. * Pr – Q2 * … * Qs) …… (1)
M’ = (Q1 * Q2 * … * Qs) – (P1 * Q2 * … * Qs) = (Q1 – P1) * Q2 * … * Qs ……………… (2)
由于T比M小,因此M’是正整数。从(1)式中我们立即看到,P1是M’的一个质因子。注意到M’比M小,
因此它的质因数分解方式应该是唯一的,可知P1也应该出现在表达式(2)中。既然P1比所有的Q都要小,
因此它不可能恰好是(2)式中的某个Q,于是只可能被包含在因子(Q1-P1)里。但这就意味着,(Q1-P1)/P1除得尽,
也就是说Q1/P1-1是一个整数,这样Q1/P1也必须得是整数。我们立即看出,P1必须也是Q1的一个因子,
这与Q1是质数矛盾了。这说明,我们最初的假设是错误的。
分解素因数:n=(p1^a1)*(p2^a2)*...*(pn*an).(分解方式唯一)
n的约数个数为ans(n)=(1+a1)*(1+a2)*...*(1+an).
a1,a2,这些分别是素数因子的幂次数因为当我的a1=3 时,那n的因子肯定会有 p1^0 p1^1 p1^2 p1^3 这四个数然后再和p2的个数搭配起来就是两个数的因子数相乘了 p1^x可以与 p2^y 随意搭配,所以进行乘法
Code--唯一分解定理
///唯一分解定理 时间复杂度为O(根号N)
int p[N],c[N];
void divide(int n)///质因数分解
{
int m=0;
for(int i=2;i<=n;i++)
{
if(n%i==0)///i是质数
{
p[++m]=i,c[m]=0;
while(n%i==0)///除掉所有的i
n/=i,c[m]++;
}
}
if(n>1)///最后一个质数 也就是最大的质数
p[++m]=n,c[m]=1;
for(int i=1;i<=m;i++)
printf("%d^%d ",p[i],c[i]);
}
Code--A题
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
typedef long long ll;
const ll N=1e6+10;
using namespace std;
ll p[N],prime[N],k,num;
void sss()
{
k=0;
for(ll i=2; i<=N; i++)
{
if(p[i]==0)
{
prime[k++]=i;///素数
for(ll j=i*2; j<=N; j+=i)
p[j]=1;///合数
}
}
}///素数筛
ll f(ll a)///a的素因子个数
{
///n的约数个数为ans(n)=(1+a1)*(1+a2)*...*(1+an).
num=1;
for(ll i=0; i<k&&prime[i]<a; i++)
{
int x=0;
while(a%prime[i]==0)
{
x++;
a/=prime[i];
}
num*=x+1;
}
if(a>1)num*=1+1;
return num;
}
int main()
{
sss();
int t,o=1;
scanf("%d",&t);
while(t--)
{
ll a,b;
scanf("%lld %lld",&a,&b);
{
if(a<b*b)
printf("Case %d: 0\n",o++);
else
{
ll ans=f(a)/2;
for(ll i=1; i<b; i++)
if(a%i==0)
ans--;
printf("Case %d: %lld\n",o++,ans);
}
}
}
return 0;
}
B题:
题意:求安然抵达彼岸的方法数,n是河宽。列出几个数,找规律也可,动态规划转移方程也可
思路:第n项应该分开考虑n-1和n-2是否相同颜色的情况
(1)如果相同那么n就可以没有顾虑的选择3种中的任意一种,则等价于n-2所有的取值所以a[n-2]*3
(2)如果不同那么等价于a[n-1]-a[n-2],即全部减去相同的的那么都应该是不同的,这一部分考虑的就是第n必选择一个和前两相等,那么就应该是(a[n-1]-a[n-2])*2
dp和贪心的不同之处在于每一次的贪心都是做出不可撤回的决策(即每次局部最优),
而在dp中还有考察每个最优决策子序列中是否包含最优决策子序列,即是否具有最优子结构性质,
贪心中每一步都只顾眼前最优,并且当前的选择是不会依赖以前的选择的,
而dp,在选择的时候是从以前求出的若干个与本步骤相关的子问题中选最优的那个,
加上这一步的值来构成这一步那个子问题的最优解
Code--B题
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const int N=1e5+5;
int a[55];
void dp()
{
a[1]=3,a[2]=9;
for(int i=3; i<=40; i++)
a[i]=a[i-2]*3+(a[i-1]-a[i-2])*2;
}
int main()
{
dp();
int c;
scanf("%d",&c);
while(c--)
{
int n;
scanf("%d",&n);
printf("%d\n",a[n]);
}
return 0;
}
C题:hdu-1496
题意:给你a,b,c,d这4个数的值,然后问a*x1^2 + b*x2^2 + c*x3^2 + d*x4^2 = 0的(x1,x2,x3,x4)解一共有多少种
第一种——>构建哈希表,优化暴力求解方程;
#include<stdio.h>
#include<string.h>
#include<algorithm>
typedef long long ll;
const ll N=2e6+10;
using namespace std;
int ha[N],p[110];
int main()
{
int a,b,c,d;
for(int i=1; i<101; i++)
p[i]=i*i;
while(~scanf("%d %d %d %d",&a,&b,&c,&d))
{
memset(ha,0,sizeof(ha));
if((a>0&&b>0&&c>0&&d>0)||(a<0&&b<0&&c<0&&d<0))///!!
{
printf("0\n");
continue;
}
for(int i=1; i<101; i++)
for(int j=1; j<101; j++)
ha[a*p[i]+b*p[j]+1000000]++;
int sum=0;
for(int i=1; i<101; i++)
for(int j=1; j<101; j++)
sum+=ha[-c*p[i]-d*p[j]+1000000];
printf("%d\n",sum*16);///每个数都可正可负 16中情况
}
return 0;
}
第二种——>分开两部分求和,若两部分的和是0,则就相加那么多种,最后乘以16。这样就能从n^4(4层for循环)变成2*n^2(2层for循环);
#include<stdio.h>
#include<string.h>
#include<algorithm>
typedef long long ll;
const ll N=1e6+10;
using namespace std;
int z[N],f[N];///正 负
int main()
{
int a,b,c,d;
while(~scanf("%d %d %d %d",&a,&b,&c,&d))
{
memset(z,0,sizeof(z));
memset(f,0,sizeof(f));
if((a<0&&b<0&&c<0&&d<0)||(a>0&&b>0&&c>0&&d>0))
{///全正全负肯定无解
printf("0\n");
continue;
}
for(int i=1; i<101; i++)
for(int j=1; j<101; j++)
{
int k=a*i*i+b*j*j;
if(k>=0)z[k]++;///k=0也要考虑在内
/// 因为a,b可能一正一负相加最后为0;
if(k<0)f[-k]++;
}
int sum=0;
for(int i=1; i<101; i++)
for(int j=1; j<101; j++)
{
int k=c*i*i+d*j*j;
if(k<=0)sum+=z[-k];
if(k>0)sum+=f[k];
}
printf("%d\n",sum*16);///2^4=16每个数都可正可负 16种情况
}
return 0;
}
D题:Leading and Trailing---前导和尾随
题意:n^k的前三位和后三位
思路:后三位快速幂就行了,关键是这个前三位。
设x = log10 (n^k) = k * log10(n),那么10^x = n^k
将x = a(整数) + b(小数),a决定了10^k的位数,b决定了10^k的具体数字
任意数可以转化成 X = 10^( x + y ) (x为整数,y为小数) 其中 10^x 来控制的是原数字,
而具体这个数字等于多少,全靠10^y ,那么 我们就可知道 10^y 就是我们要求的前n个数字
还不会炸 long long (用double的话末尾消去,很适合)这样我们就能保证前7位可知,
如果要前三位 只需要 10^(y) * 100 就好了。
前三位: 设x = log (n^k) = k * log10(n), 那么10^x = n^k.将x = a(整数) + b(小数),
整数部分10^a是小数点的位置,并不影响前三位数字。 故只需要求出10^b取前三位。
使用fmod(a, 1)表示求浮点型数 a 的小数部分
Code--快速幂
///#include <math.h> pow() 函数用来求 x 的 y 次幂,
///快速幂O(logn)
///由于指数函数是爆炸增长的函数,所以很有可能会爆掉int的范围
int power(int a,int b)
{
int ans=1,base=a;
while(b!=0)
{
if(b&1)///表示是奇数
ans*=base;
///x&1==0为偶,x&1==1为奇
base*=base;
b>>=1;///二进制右移1位。最高位补二进制0
}
return ans;
}
Code--D题
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
typedef long long ll;
const int N=1e6+10;
using namespace std;
ll qsm(ll n,ll x,ll mod)
{
ll res=1;
if(x==0)return 1;
res=qsm(n*n%mod,x/2,mod);
if(x&1)res=res*n%mod;
return res;
}
int main()
{
int t;
scanf("%d",&t);
int flag=1;
while(t--)
{
ll n,k;
scanf("%lld %lld",&n,&k);
int ans1,ans2;
ans1=(int)pow(10.0,2.0+fmod(k*log10(n),1.0));
ans2=(int)qsm(n,k,1000);
printf("Case %d: %d %03d\n",flag++,ans1,ans2);
}
return 0;
}
///n^k的前三位和后三位
E题:
题意:某个车站有N个火车车厢,编号为1~N,每个车厢上有xi个人,现在有三个火车头,他们能拉最多m个车厢(m<=N/3),而且这m个车厢的编号要连续的。问这三个火车头最多能拉多少个人
思路:因为m<=N/3, 所以按照贪心的思想,为了拉更多的人,每个火车头一定是要拉m个连续的车厢。为了求某段连续的车厢共有多少人,可以前缀和预处理, 某一段和=sum[ i ] - sum[ i-m].
f[i][j] 代表前i个车厢,用j个火车头拉,最多能拉多少人。对于第i个车厢,如果当前这个车头选择要拉这个车厢,那么要把以i为最后一个车厢的连续m个车厢一起拉,所以状态转移方程是:f[i][j] = max(f[i-1][j], f[i-m][j-1]+sum[i]-sum[i-m]);
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int sum[50050],a[50050];
int dp[50050][4];///前i节车厢用j个火车头拉,最多能拉多少人
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(sum,0,sizeof(sum));
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
int n,m;
scanf("%d",&n);///火车有几节车厢
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);///用来存每节火车里面有的人数
sum[i]=sum[i-1]+a[i];///前i节车厢人数
}
scanf("%d",&m);///最多可以推动几节火车
for(int i=m;i<=n;i++)
{
for(int j=3;j>=1;j--)
{///每种状态面临两种取法 取或者不取 以第i节车厢为首的m节车厢
dp[i][j]=max(dp[i-1][j],dp[i-m][j-1]+(sum[i]-sum[i-m]));
}///不选(不要第i个车厢) 选(前i-m个车厢用j-1个车头来拉+m个车厢有的人数)
}
printf("%d\n",dp[n][3]);
}
return 0;
}
F题:Pair of Topics (一队主题)
题意:i<j ,ai+aj>bi+bj 被叫做good topics,问有多少个good topics?变形得 (ai-bi)+(aj-bj)>0 创建一个c数组 c[i]=a[i]-b[i] ,然后对c数组排序,就变成当i<j 时,求 c[i]-c[j]>0的个数
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2*1e5+10;
#define mem(a,b) memset(a,b,sizeof(a))
ll a[N],b[N],c[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(int i=1; i<=n; i++)
scanf("%lld",&b[i]);
int cnt=0,x=0;
for(int i=1; i<=n; i++)
{
c[i]=a[i]-b[i];
if(c[i]<=0)
cnt++;
}
if(cnt==n)
printf("0\n");
else
{
sort(c+1,c+n+1);
ll ans=0;
ll l=1,r=n;
while(l<r)
{
if(c[r]+c[l]>0)
{
ans+=r-l;
r--;
}
else l++;
}
printf("%lld\n",ans);
}
return 0;
}
G题--By Elevator or Stairs? (乘电梯还是楼梯?)cf-1249E
题意:n层楼,a[i] (0<i<n)表示从 i 楼到 i + 1 楼走楼梯的时间,b[i] (0<i<n)表示从 i 楼到 i + 1 楼乘电梯的时间,其中每一次乘电梯需要等待 c 时间,求从1楼到 1 ~ n 层楼所需要的最短时间
思路:二维数组dp[i][j],表示通过 j 的方法( j = 0 表示楼梯,j = 1表示电梯)到第 i 层所需的最少时间。
所以得出状态转移方程dp[i][0]=min(dp[i-1][0] + a[i] , dp[i-1][1] + a[i]);dp[i][1]=min(dp[i-1][1] + b[i], dp[i-1][0] + b[i] + c);
dp[1][0]=0, dp[1][1]=1e18;然后就是输出min(dp[i][0], dp[i][1])
#include<stdio.h>
#include<string.h>
#include<algorithm>
typedef long long ll;
const int N=2*1e5+10;
using namespace std;
int a[N],b[N],dp[N][10];
///二维数组dp[i][j],表示通过 j 的方法
///(j=0表示楼梯,j=1表示电梯)到第i层所需的最少时间。
int main()
{
int n,c;
while(~scanf("%d %d",&n,&c))
{
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=2; i<=n; i++)
scanf("%d",&a[i]);
for(int i=2; i<=n; i++)
scanf("%d",&b[i]);
dp[1][0]=0;///stairs
dp[1][1]=0x3f3f3f;///elevator
///a[i] 代表 i-1到i层楼的时间
for(int i=2; i<=n; i++)
{
dp[i][0]=min(dp[i-1][0]+a[i],dp[i-1][1]+a[i]);
dp[i][1]=min(dp[i-1][0]+b[i]+c,dp[i-1][1]+b[i]);
}
for(int i=1; i<n; i++)
printf("%d ",min(dp[i][0],dp[i][1]));
printf("%d\n",min(dp[n][0],dp[n][1]));
}
return 0;
}
H题--cf-1244EMinimizing Difference (极小化差)
题意:求最小和最大的极差,经过k次变换,使得极差最小,且每次改变只能增1或减1;思路:两边同时增1和减1,计算在k次变换范围内的极差,如果不够改变,if判断一下能否改变一端,使得极差变小
注意:long long n,k,x,ans;不要放在主函数里,会越界,应该放上面;
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll a[N];
ll n,k,x,ans;
int main()
{
scanf("%lld %lld",&n,&k);
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
sort(a+1,a+n+1);
for(int i=1; i<=n/2; i++)
{
int j=n-i+1;
x=(a[i+1]-a[i]+a[j]-a[j-1])*i;
if(x<=k)
k-=x;
else
{
ans=a[j]-a[i]-k/i;
break;
}
}
printf("%lld\n",ans);
return 0;
}
各位Acmer加油!