1.a(a.cpp/c/pas)
时空限制
时间限制
1s
空间限制
256MB
【问题描述】
你是能看到第一题的 friends呢。—— hja
世界上没有什么比卖的这么贵的弹丸三还令人绝望的事了,所以便有了这么一道题。定义
f(x)
为满足
(a×b)∣x
的有序正整数对
(a,b)
的个数。
现在给定
N
,求
【输入格式】
一行个整数 N 。
【输出格式】
一行个整数代表答案 。
【样例输入】
6
【样例输出】
25
【数据范围与规定】
对于
对于
60%
的数据,
1≤N≤1000
对于
100%
的数据,
1≤N≤1011
solution
60分思路
- 胡乱找一下规律,发现 ans=∑i=1N(n/i)∗d(i)
其中 d(i) 表示 i 的约数个数
然后
n/i 可以除法分块, d(i) 的前缀和也可以除法分块,然后乱搞一下就好了。怎么推出来并不重要反正只有60分
100分思路
- 如果一个正整数对 (a,b) 满足 (a×b)∣x ,那就有 x=a×b×c
- 那问题就转换成了找三个正整数 a,b,c ,满足 a×b×c=x 的方案数
- 因为题目求的是 ∑i=1Nf(i) ,所以 a×b×c≤N 就行,因为只要 a×b×c≤N ,这三个正整数就对答案有贡献
- 所以枚举 a,b,c ,满足条件, ans++ ,但这样是 O(n3) ,稳 TLE
- 考虑一种满足条件的
a,b,c
,这三个整数有
6
种排列,而且这
6 种排列都满足条件 - 所以可以强行让 a≤b≤c ,这样枚举并且统计答案,最后再 ∗6 就好了
但是还有一种情况是 a,b,c 三个数有可能有相同的数,所以再把这些情况枚举一下
两个数相同,一共有 3 种排列,三个数相同,只有
1 种排列最后把这些情况都加起来就是最后的答案,时间复杂度
据zhx说是 O(n56)
code
- test 60分代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
template<typename T>
void input(T &x) {
x=0; T a=1;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
x*=a;
return;
}
ll n;
ll Getd(ll n) {
ll ret=0;
for(ll l=1,r;l<=n;l=r+1) {
r=n/(n/l);
ret+=(n/l)*(r-l+1);
}
return ret;
}
ll sum(ll n) {
ll ret=0;
for(ll l=1,r;l<=n;l=r+1) {
r=n/(n/l);
ret+=(n/l)*(Getd(r)-Getd(l-1));
}
return ret;
}
int main() {
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
input(n);
printf("%lld\n",sum(n));
fclose(stdin);
fclose(stdout);
return 0;
}
- 100分code
其实也没那么难
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int main() {
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
ll n;
scanf("%lld",&n);
ll ans=0,tans=0;
/*
for循环里面的ans++就是统计的三个数都相同的情况
*/
for(ll a=1,v;a*a<=(v=n/a);a++,ans++)
for(ll b=a+1;b*b<=v;b++)
tans+=n/(a*b)-b;
/*
tans+=的那个东西,是c的所有取值的情况
a*b*c<=n
c<=n/(a*b)
因为我们规定了a<=b<=c
所以b<=c<=n/(a*b)
所以c的取值一共有n/(a*b)-b种
*/
ans+=tans*6;
tans=0;
/*
下面是两个数相同的情况
和上面类似
*/
for(ll a=1,v;(v=a*a)<=n;a++) {
tans+=n/v;
if(a*a<=n/a) tans--;
}
ans+=tans*3;
printf("%lld\n",ans);
return 0;
}
2.b(b.cpp/c/pas)
时空限制
时间限制
1s
空间限制
256MB
【问题描述】
你是能看到第二题的 friends呢。—— laekov
Hja和 Yjq为了抢男主角打了起来,现在他们正在一棵树上决斗 。Hja在 A点,Yjq在 B点,Hja先发制人开始移动 。每次他们可以沿着一条边移动 ,但 一旦一条边被对方走过了自己就不能再走这条边了。每条边上都有权值 ,他们都希望自己的权值尽量多 。现在给你这棵树以及他们俩开始的位置 ,问Hja能够获得的最大权值 。
【输入格式】
第一行两个整数
N,M
,代表树的点数和询问的个数 。
接下来
N−1
行每行三个整数
a,b,c
,代表从
a
到
接下来
【输出格式】
对于每次询问 ,输出一个整数代表答案 。
【样例输入1】
2 1
1 2 3
1 2
【样例输出1】
3
【样例输入2】
3 2
1 2 3
1 3 1
2 3
1 3
【样例输出2】
3
4
【数据范围与规定】
对于
30%
的数据 ,
1≤N,M≤1000
。
对于另外
30%
的数据 ,
M=1
。
对于
100%
的数据,
1≤N,M≤105,0≤c≤103,1≤a,b,A,B≤N
solution
code
- std by zhx
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,m,en,z[maxn*3],f[maxn][20],q[maxn],depth[maxn],sum[maxn*3][2],fd[maxn],start[maxn],end[maxn],value[maxn];
struct edge
{
int e,d;
edge *next;
}*v[maxn],ed[maxn<<1];
void add_edge(int s,int e,int d)
{
en++;
ed[en].next=v[s];v[s]=ed+en;v[s]->e=e;v[s]->d=d;
}
int get(int p,int d)
{
if (d==-1) return p;
int x=0;
while (d)
{
if (d&1) p=f[p][x];
d>>=1;
x++;
}
return p;
}
int get_lca(int p1,int p2)
{
if (depth[p1]<depth[p2]) swap(p1,p2);
p1=get(p1,depth[p1]-depth[p2]);
int x=0;
while (p1!=p2)
{
if (!x || f[p1][x]!=f[p2][x])
{
p1=f[p1][x];
p2=f[p2][x];
x++;
}
else x--;
}
return p1;
}
int calc(int p1,int p2)
{
if (p1==f[p2][0]) return value[1]-value[p2];
else return value[p1]+fd[p1];
}
int calcp(int p,int v)
{
int l=start[p]-1,r=end[p];
while (l+1!=r)
{
int m=(l+r)>>1;
if (v>z[m]) l=m;
else r=m;
}
return r;
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%d%d",&n,&m);
int tot=0;
for (int a=1;a<n;a++)
{
int s,e,d;
scanf("%d%d%d",&s,&e,&d);
tot+=d;
add_edge(s,e,d);
add_edge(e,s,d);
}
depth[1]=1;
int front=1,tail=1;
q[1]=1;
for (;front<=tail;)
{
int now=q[front++];
for (edge *e=v[now];e;e=e->next)
if (!depth[e->e])
{
depth[e->e]=depth[now]+1;
fd[e->e]=e->d;
f[e->e][0]=now;
int p=now,x=0;
while (f[p][x])
{
f[e->e][x+1]=f[p][x];
p=f[p][x];
x++;
}
q[++tail]=e->e;
}
}
int cnt=0;
for (int a=n;a>=1;a--)
{
int now=q[a];
start[now]=cnt+1;
for (edge *e=v[now];e;e=e->next)
if (depth[e->e]==depth[now]+1)
{
z[++cnt]=value[e->e]+e->d;
value[now]+=value[e->e]+e->d;
}
z[++cnt]=tot-value[now];
end[now]=cnt;
sort(z+start[now],z+end[now]+1);
sum[end[now]][0]=z[end[now]];
sum[end[now]][1]=0;
for (int a=end[now]-1;a>=start[now];a--)
{
sum[a][0]=sum[a+1][0];
sum[a][1]=sum[a+1][1];
if ((a&1)==(end[now]&1)) sum[a][0]+=z[a];
else sum[a][1]+=z[a];
}
cnt++;
}
for (int a=1;a<=m;a++)
{
int p1,p2;
scanf("%d%d",&p1,&p2);
int lca=get_lca(p1,p2);
int dist=depth[p1]+depth[p2]-2*depth[lca];
int delta=dist/2+(dist&1);
int px,px1,px2;
if (depth[p1]-depth[lca]<delta) px=get(p2,dist-delta);
else px=get(p1,delta);
if (depth[p1]-depth[lca]<delta-1) px1=get(p2,dist-delta+1);
else px1=get(p1,delta-1);
if (depth[p2]-depth[lca]<dist-delta-1) px2=get(p1,delta+1);
else px2=get(p2,dist-delta-1);
int ans=0;
if (p1==px)
{
if (p2==px) ans=sum[start[px]][0];
else
{
int v2=calc(px2,px);
int p=calcp(px,v2);
ans=sum[p+1][0]+sum[start[px]][1]-sum[p][1];
}
}
else
{
if (p2==px)
{
int v1=calc(px1,px);
int p=calcp(px,v1);
ans=v1+sum[p+1][1]+sum[start[px]][0]-sum[p][0];
}
else
{
int v1=calc(px1,px);
int pp1=calcp(px,v1);
int v2=calc(px2,px);
int pp2=calcp(px,v2);
if (pp2==pp1) pp2++;
if (pp1>pp2) swap(pp1,pp2);
ans=v1+sum[pp2+1][dist&1]+sum[pp1+1][1-(dist&1)]-sum[pp2][1-(dist&1)]+sum[start[px]][dist&1]-sum[pp1][dist&1];
}
}
printf("%d\n",ans);
}
return 0;
}
3.c(c.cpp/c/pas)
时空限制
时间限制
1s
空间限制
256MB
【问题描述】
你是能看到第三题的 friends呢。—— aoao
Yjq买了36个卡包,并且把他们排列成6×6的阵型准备开包 。左上角是(0,0),右下角为 (5,5)。为了能够开到更多的金色普通卡,Yjq会为每个包添加 1-5个玄学值,每个玄学值可以是 1-30中的一个整数。但是不同的玄学值会造成不同的欧气加成,具体如下:
- 同一个卡包如果有两个相同的玄学值则会有无限大的欧气加成。
- 同一个卡包如果有两个相邻的玄学值会有 A 点欧气加成。
- 相邻的两个卡包如果有相同的玄学值会有
B 点欧气加成。 - 相邻的两个卡包如果有相邻的玄学值会有 C 点欧气加成。
- 距离为2的卡包如果有相同玄学值会有
D 点欧气加成。 - 距离为2的卡包如果有相邻玄学值会有 E 点欧气加成。
以上的所有的加成是每存在一个符合条件就会加一次,,如卡包1,2,3的玄学值就会加两次。
但是,玄学值个不可控的东西,即使Yjq也只能自己决定
【输入格式】
输入的第一行有 5个整数
A,B,C,D,E
。
接下去有
6×6
的代表初始玄学值。
每个玄学值为
[n:a1,a2,⋯,an]
的描述形式。
【输出格式】
一行个整数代表答案。
【样例输入】
5 4 3 2 1
[1:1][2][3][4][5][6]
[1:1][2][3][4][5][6]
[1:1][2][5:1,2,3,4,5][5][6]
[1:1][ 1:2][5:1,2,3,4,5][5][6]
[1:1][2][3][4][5][6]
[1:1][2][3][4][5][6]
【样例输出】
250
【数据规模与约定】
对于 100% 的数据, 1≤A,B,C,D,E≤100,1≤n≤5,1≤ai≤30 。有部分分。
solution
就是一个简单的九维dp而已
code
- std by zhx
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define now pre[a][b][c][d][e][s1][s2][s3][s4]
#define dis(a,b,c,d) (abs(a-c)+abs(b-d))
const int INF=0x3f3f3f3f;
int A,B,C,D,E,num[10][10],value[10][10][10],delta[10][10][40],dp[31][6][6][6][6][2][2][2][2];
char s[500];
bool map[6][6][6][6];
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d%d%d%d",&A,&B,&C,&D,&E);
for (int a=0;a<6;a++)
{
scanf("%s",s);
int p=0;
for (int b=0;b<6;b++)
{
int px=p;
while (s[px]!=']')
px++;
p++;
num[a][b]=s[p]-'0';
p++;
p++;
for (int c=1;c<=num[a][b];c++)
{
int v=0;
while (s[p]>='0' && s[p]<='9')
{
v=v*10+s[p]-'0';
p++;
}
value[a][b][c]=v;
p++;
}
p=px+1;
}
}
int base=0;
for (int a=0;a<6;a++)
for (int b=0;b<6;b++)
if (a>=2 && a<=3 && b>=2 && b<=3) ;
else
{
sort(value[a][b]+1,value[a][b]+num[a][b]+1);
for (int c=2;c<=num[a][b];c++)
if (value[a][b][c]-value[a][b][c-1]==1) base+=A;
for (int c=2;c<=3;c++)
for (int d=2;d<=3;d++)
{
if (dis(a,b,c,d)==1)
{
for (int e=1;e<=num[a][b];e++)
{
delta[c][d][value[a][b][e]]+=B;
delta[c][d][value[a][b][e]-1]+=C;
delta[c][d][value[a][b][e]+1]+=C;
}
}
if (dis(a,b,c,d)==2)
{
for (int e=1;e<=num[a][b];e++)
{
delta[c][d][value[a][b][e]]+=D;
delta[c][d][value[a][b][e]-1]+=E;
delta[c][d][value[a][b][e]+1]+=E;
}
}
}
for (int c=0;c<6;c++)
for (int d=0;d<6;d++)
if (dis(a,b,c,d)<=2 && (c!=a || d!=b) && !map[a][b][c][d])
{
map[a][b][c][d]=map[c][d][a][b]=true;
if (c>=2 && c<=3 && d>=2 && d<=3) ;
else
{
int dist=dis(a,b,c,d);
for (int e=1;e<=num[a][b];e++)
for (int f=1;f<=num[c][d];f++)
{
if (abs(value[a][b][e]-value[c][d][f])==0)
{
if (dist==1) base+=B;
else base+=D;
}
if (abs(value[a][b][e]-value[c][d][f])==1)
{
if (dist==1) base+=C;
else base+=E;
}
}
}
}
}
memset(dp,0x3f,sizeof(dp));
dp[0][0][0][0][0][0][0][0][0]=base;
for (int a=0;a<30;a++)
for (int b=0;b<=num[2][2];b++)
for (int c=0;c<=num[2][3];c++)
for (int d=0;d<=num[3][2];d++)
for (int e=0;e<=num[3][3];e++)
for (int s1=0;s1<=1;s1++)
for (int s2=0;s2<=1;s2++)
for (int s3=0;s3<=1;s3++)
for (int s4=0;s4<=1;s4++)
if (dp[a][b][c][d][e][s1][s2][s3][s4]!=INF)
{
int v=dp[a][b][c][d][e][s1][s2][s3][s4];
for (int sx1=0;sx1<=(b!=num[2][2]);sx1++)
for (int sx2=0;sx2<=(c!=num[2][3]);sx2++)
for (int sx3=0;sx3<=(d!=num[3][2]);sx3++)
for (int sx4=0;sx4<=(e!=num[3][3]);sx4++)
{
int wmt=0;
if (sx1)
{
wmt+=delta[2][2][a+1];
if (s1) wmt+=A;
if (s2) wmt+=C;
if (s3) wmt+=C;
if (s4) wmt+=E;
}
if (sx2)
{
wmt+=delta[2][3][a+1];
if (s1) wmt+=C;
if (s2) wmt+=A;
if (s3) wmt+=E;
if (s4) wmt+=C;
}
if (sx3)
{
wmt+=delta[3][2][a+1];
if (s1) wmt+=C;
if (s2) wmt+=E;
if (s3) wmt+=A;
if (s4) wmt+=C;
}
if (sx4)
{
wmt+=delta[3][3][a+1];
if (s1) wmt+=E;
if (s2) wmt+=C;
if (s3) wmt+=C;
if (s4) wmt+=A;
}
if (sx1 && sx2) wmt+=B;
if (sx1 && sx3) wmt+=B;
if (sx1 && sx4) wmt+=D;
if (sx2 && sx3) wmt+=D;
if (sx2 && sx4) wmt+=B;
if (sx3 && sx4) wmt+=B;
int &t=dp[a+1][b+sx1][c+sx2][d+sx3][e+sx4][sx1][sx2][sx3][sx4];
if (t>v+wmt) t=v+wmt;
}
}
int ans=INF;
for (int a=0;a<=1;a++)
for (int b=0;b<=1;b++)
for (int c=0;c<=1;c++)
for (int d=0;d<=1;d++)
ans=min(ans,dp[30][num[2][2]][num[2][3]][num[3][2]][num[3][3]][a][b][c][d]);
printf("%d\n",ans);
return 0;
}
除了T1都很可做的