2025/5/4测试

AT_abc172_d [ABC172D] Sum of Divisors

题目描述

题意翻译

  • \(f(x)\) 表示正整数 \(x\) 的因数个数
  • 现在给你一个正整数 $ N $ ,让你求出 $ \sum_{K=1}^N\ K\times\ f(K) $ 。


输入格式

输出格式

  • 共一行,输出 $ \sum_{K=1}^N\ K\times\ f(K) $ 的结果
  • 最后别忘了输出回车

输入输出样例 #1

输入 #1

4

输出 #1

23

输入输出样例 #2

输入 #2

100

输出 #2

26879

输入输出样例 #3

输入 #3

10000000

输出 #3

838627288460105

说明/提示

  • $ 1\ \leq\ N\ \leq\ 10^7 $
  • $f(1)=1 $ , $ f(2)=2 $ , $ f(3)=2 $ , $ f(4)=3 $ , 所以答案为 $ 1\times\ 1\ +\ 2\times\ 2\ +\ 3\times\ 2\ +\ 4\times\ 3\ =23 $ 。

Translated by qinmingze

思路+AC代码

此题我知道的共有四种算法(如果打表也是一种算法

算法1:我们看到约数个数很容易想到线性筛求约数个数,时间还是比较可观的(但前提是你得记住板子,好吧其实我没记住),我认为代码无须多言了。

AC code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
const int N=1e7+10; 
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}

int n,d[N],prime[N],vis[N],num[N],ans,cnt;

void get(int n)
{
	d[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!vis[i])
		{
			prime[++cnt]=i;
			d[i]=2;
			num[i]=1;
		}
		for(int j=1;i*prime[j]<=n;j++)
		{
			int m=i*prime[j];
			vis[m]=1;
			if(i%prime[j]==0)
			{
				d[m]=d[i]/(num[i]+1)*(num[i]+2);
				num[m]=num[i]+1;
				break;
			}
			else
			{
				d[m]=d[i]*2;
				num[m]=1;
			}
		}
	}
}

signed main()
{
	n=read();
	get(n);
	for(int i=1;i<=n;i++) ans+=i*d[i];
	write(ans) ;
	return 0;
}

算法2:如果你忘了线性筛求约数个数想硬求是绝对会超时的,不过我们可以采用逆向思维,考虑每个数对它的倍数的贡献。即枚举1~n,令i的小于等于n的倍数的因子数+1.但是这种算法的时间复杂度显然高于线性筛.

AC code
#include <bits/stdc++.h>
using namespace std;

#define int long long

int n;
int factor[10000005];
int res;

signed main()
{
    freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	cin >> n;
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = i; j <= n; j += i )
		{
			factor[j] ++ ;	
		}
	}
	for (int i = 1; i <= n; i ++ )
		res += factor[i] * i;
	cout << res;
	return 0;
}

算法3:接着考虑一个数对它的倍数的贡献.设一个数为x,则x的贡献为x+2x+3x+4x...(这里指k*f(k)而不单指f(k)),我们发现这不就是一个等差数列吗?且公差为x,首项为x,项数为\(\lfloor \tfrac nx \rfloor\),末项为\(\lfloor \tfrac nx \rfloor\)×\(x\).根据等差数列求和公式即可算得.时间复杂度为\(O\)(\(n\)),十分优秀!(马亮也是十分短小精悍)

AC code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}

int ans,n;

signed main()
{
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
		ans+=(i+n/i*i)*(n/i)/2;
	cout<<ans;
	return 0;
}

算法4:分块打表(我不会

  • T2 匹配


思路+AC代码

本题一眼hashing+二分,挂了...

实际上它并不能用二分,因为二分要满足单调性,但本题并不是说长度长的符合条件短的一定符合条件,因此不能用二分.

正解:其实就是纯hashing,不要忘了unsigned long long.

AC code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
#define ull unsigned long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
const int N=4e5+10;
const ull P=131;
//inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}

ull ha[N],hb[N],p[N];
int ans,la,lb,t;
string a,b;
char c;

ull get(ull h[],int l,int r)
{
	return h[r]-h[l]*p[r-l];
}

bool check(int x)
{
	if(get(ha,0,x)==get(hb,lb-x,lb)) return true;
	return false;
}

signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	scanf("%lld",&t);
	p[0]=1;
	for(int i=1;i<=N;i++)p[i]=p[i-1]*P;
	while(t--)
	{	
		memset(ha,0,sizeof(ha));
		memset(hb,0,sizeof(hb));
		cin>>la>>lb;
		cin>>a;cin>>c;
		b=a.substr(0,lb)+c;
		lb++;
		
		for(int i=1;i<=la;i++) ha[i]=ha[i-1]*P+a[i-1];
		for(int i=1;i<=lb;i++) hb[i]=hb[i-1]*P+b[i-1];
		
		ans=0;
		for(int k=1;k<=la&&k<=lb;k++)
		{
			if(check(k)) ans=max(ans,k);
		}
	    cout<<ans;puts("");
	}
	return 0;
}
  • T3 回家(的诱惑)



思路+暴力部分分+AC代码

算法1:暴力搜索,dfs删点,30pts

TLE code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
const int N=4e5+10;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}

int t,n,m,vis[N],ans;
vector<int> e[N];
priority_queue <int,vector<int>,greater< int> > q;

bool dfs(int s,int t,int cut)
{
	if(s==t)return 1;
	vis[s]=1;
	for(int v:e[s])
	{
		if(vis[v])continue;
		if(v==cut)continue;
		if(dfs(v,t,cut))return 1;
	}	
	return 0;
}

signed main()
{
	freopen("home.in","r",stdin);
	freopen("home.out","w",stdout);
	t=read();
	while(t--)
	{
		memset(e,0,sizeof(e));
		ans=0;
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			int u=read(),v=read();
			if(u!=v) e[u].push_back(v),e[v].push_back(u);
		}
		for(int i=2;i<n;i++)
		{
			memset(vis,0,sizeof(vis));
			if(!dfs(1,n,i))
				ans++,q.push(i);
		}
		write(ans);puts("");
		while(!q.empty())
		{
			int x=q.top();
			write(x);cout<<" ";
			q.pop();
		}
		puts("");
	}
	return 0;
}

正解:其实不难想到割点,但问题是并不是每个割点都是必经之路,例如:

这里2和3都属于割点,但都不是必经之路.因为一个割点不一定把1号点和n号点分到两个不同的块去.不过可以肯定的是必经之路肯定是割点.

其实只要略微改动一下tarjan的模板,用追溯值反应区域,因为dfn[1]=1,显然<=low[x],那么只要low[n]<=dfnx即可保证该割点分1和n到两个不同块中.

AC code
#include<bits/stdc++.h>
using namespace std;
#define re register
#define int long long
#define endl '\n'
#define lid (id<<1)
#define rid (id<<1|1)
const int N=4e5+10;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}return x*f;}inline void write(int x){if(x<0)x*=-1,putchar('-');if(x>9)write(x/10);putchar(x%10+'0');return;}inline int max(int x,int y){return (x<y)?y:x;}inline int min(int x,int y){return (x<y)?x:y;}

vector<int> e[N];
int dfn[N],low[N],cut[N],cnt,num,crab[N],cr[N],root; 
int n,m,t;

void tarjan(int x)
{
	dfn[x]=low[x]=++num;
	int flag=0;
	for(int y:e[x])
	{
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
			
			if(dfn[x]<=low[y])
			{
				flag++;
				if(x!=root||flag>1)
					if(!cut[x]&&dfn[x]<=low[n]&&x!=1&&x!=n)//注意这里的改动 
						cut[x]=true,cnt++;
			}
		}
		else 
			low[x]=min(low[x],dfn[y]);
	}
}

signed main()
{
	freopen("home.in","r",stdin);
	freopen("home.out","w",stdout);
	t=read();
	while(t--)
	{
		memset(e,0,sizeof(e));
		memset(low,0,sizeof(low));
		memset(dfn,0,sizeof(dfn));
		memset(cut,0,sizeof(cut));
		cnt=0,num=0;
		n=read();m=read();
		for(int i=1;i<=m;i++)
		{
			int u=read(),v=read();
			if(u!=v) e[u].push_back(v),e[v].push_back(u);
		}
		for(int i=1; i<=n; i++)
			if(!dfn[i]) 
			{
				root = i;
				tarjan(i);
			}
		write(cnt);puts("");
		for(int i=2;i<n;i++)
			if(cut[i])
				cout<<i<<" ";
		if(cnt)cout<<endl;
	}
	return 0;
}
posted @ 2025-05-05 14:42  Crab2016  阅读(29)  评论(0)    收藏  举报