2020级10.21练习

Hankson 的趣味题

一、题目

点此看题

二、解法

gcd ⁡ \gcd gcd lcm \text{lcm} lcm的实质是在对应的质数的指数上取 min max \text{min max} min max
我们先把小于等于 n \sqrt n n 的质数给筛出来,然后再用这些质数给 a 0 , a 1 , b 0 , b 1 a_0,a_1,b_0,b_1 a0,a1,b0,b1质因数分解,然后我们对于每一个质数考虑,注意特判大于 n \sqrt n n 的质数。
下面给我我带有注释的代码。

#include <cstdio>
const int MAXN = 50005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,val[4],a[4][MAXN],a0,a1,b0,b1;
int cnt,p[MAXN],vis[MAXN];
void sieve(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!vis[i]) p[++cnt]=i;
		for(int j=1;j<=cnt && i*p[j]<=n;j++)
		{
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
}
void dec(int num,int cur)
{
	val[cur]=0;
	for(int i=1;i<=cnt;i++)
	{
		a[cur][i]=0;
		while(num%p[i]==0)
		{
			a[cur][i]++;
			num/=p[i];
		}
	}
	a[cur][cnt+1]=0;
	if(num!=1) val[cur]=num,a[cur][cnt+1]=1;
}
int solve()
{
	int res=1;
	for(int i=1;i<=cnt+1;i++)//对于每一个质因数单独考虑 
	{
		if(a[1][i]>a[3][i]) return 0;//如果a1某一个质因数的次数大于b1,那么无解,因为a1肯定是b1的因数 
		if(a[0][i]>a[1][i] && a[2][i]<a[3][i] && a[1][i]!=a[3][i]) return 0;//如果两个皆由x的所取次数决定,判断他们需要的次数相不相等 
		if(a[0][i]==a[1][i] && a[2][i]==a[3][i]) res*=(a[3][i]-a[1][i]+1); //乘法原理,在可行区间内皆可取到 
	}
	return res;
}
int main()
{
	sieve(5e4);//欧拉筛 
	n=read();
	while(n--)
	{
		a0=read();a1=read();b0=read();b1=read();
		dec(a0,0);dec(a1,1);
		dec(b0,2);dec(b1,3);//质因数分解 
		if(val[1] && val[1]!=val[3])
		{//判断大于sqrt的质因数的情况,要么就是a1没有大于sqrt的质因数,要么就是a1和b1的那个质因数相等 
			printf("0\n");
			continue;
		}
		printf("%d\n",solve());
	}
}

定价

一、题目

点此看题

二、解法

其实这道题做法特别多,主要就是码量大。
讲一种贪心解法吧(毫无特判),就是找到 L L L最后的非零位,然后把它加1,边做边统计答案即可,其实这种贪心来源于保证0的个数单调递增是一定能取到答案的,上代码。

#include <cstdio>
#include <iostream>
using namespace std;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,ans,Min;
int change(int num)
{
	int t=1;
	while(num>0)
	{
		if(num%10) return t; 
		num/=10;
		t*=10;
	}
}
int calu(int num)
{
	int a[10]={};
	while(num>0)
	{
		a[++a[0]]=num%10;
		num/=10;
	}
	for(int i=1;i<=a[0];i++)
		if(a[i])
			return (a[0]-i+1)*2-(a[i]==5);
}
int main()
{
	n=read();
	while(n--)
	{
		Min=20;
		int L=read(),R=read();
		while(L<=R)
		{
			int t=calu(L);
			if(Min>t)
			{
				Min=t;
				ans=L;
			}
			L+=change(L);
		}
		printf("%d\n",ans);
	}
}

最优贸易

一、题目

点此看题

二、解法

我的思路就是先缩点(其实缩不缩都一样),然后再仿照最短路的方法跑 spfa \text{spfa} spfa,注意还要跑一个反向图,来判断当前点是否能到达 n n n点,才更新答案。
写的有点长,勿喷。

#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cstring>
using namespace std;
const int MAXN = 100005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,tot,Index,num,ans,a[MAXN],f[MAXN];
int dis[MAXN],Min[MAXN],Max[MAXN],dfn[MAXN],low[MAXN],mark[MAXN];
bool in[MAXN];
struct edge
{
	int v,next;
}e[MAXN*10];
stack<int> s;
vector<int> G[MAXN],rg[MAXN];
void tarjan(int u)
{
	dfn[u]=low[u]=++Index;
	s.push(u);
	in[u]=1;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(in[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		num++;
		int v;
		do
		{
			v=s.top();
			s.pop();
			in[v]=0;
			mark[v]=num;
		}while(v!=u);
	}
}
void bfs()
{
	queue<int> q;
	q.push(mark[n]);
	in[mark[n]]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=0;i<rg[u].size();i++)
		{
			int v=rg[u][i];
			if(!in[v])
			{
				in[v]=1;
				q.push(v);
			}
		}
	}
}
void spfa()
{
	memset(dis,0x3f,sizeof dis);
	queue<int> q;
	q.push(mark[1]);
	dis[mark[1]]=Min[mark[1]];
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		if(in[u]) ans=max(ans,Max[u]-dis[u]);
		for(int i=0;i<G[u].size();i++)
		{
			int v=G[u][i];
			if(dis[v]>min(dis[u],Min[v]))
			{
				dis[v]=min(dis[u],Min[v]);
				q.push(v);
			}
		}
	}
}
int main()
{
	memset(Min,0x3f,sizeof Min);
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),z=read();
		e[++tot]=edge{v,f[u]},f[u]=tot;
		if(z==2) e[++tot]=edge{u,f[v]},f[v]=tot;
	} 
	for(int i=1;i<=n;i++)
		if(!dfn[i])
			tarjan(i);
	for(int i=1;i<=n;i++)
	{
		Min[mark[i]]=min(Min[mark[i]],a[i]);
		Max[mark[i]]=max(Max[mark[i]],a[i]);
		for(int j=f[i];j;j=e[j].next)
		{
			int v=e[j].v;
			if(mark[i]!=mark[v])
			{
				rg[mark[v]].push_back(mark[i]);
				G[mark[i]].push_back(mark[v]);
			}
		}
	}
	bfs();
	spfa();
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值