191029-CSP模拟DAY1

今天又差点垫底,下次要加油啊。

T1:数列

传送门

题意:给一个序列,对每一个数进行±a或±b的操作,求最小操作次数。可以轻松(其实并不轻松 )地看出是一道解不定方程的题,即给定一个数w,求一组满足ax+by=w的解,且x和y的绝对值之和最小。x,y的通集为 ( x + k b g c d ( a , b ) , y − k a g c d ( a , b ) ) \left(x+k \frac{b}{g c d(a, b)}, y-k \frac{a}{g c d(a, b)}\right) (x+kgcd(a,b)b,ykgcd(a,b)a);

想到这里,可以自己造几组数据推一下(因为我不会证明 ),发现要让x−nb接近于0 或者令y+na接近于0,才存在最优的一组合法解。由此,我们先保证a>b,则尽量选a最优,再用扩展欧几里得算法求出一组任意解,有一个小技巧,我们先求出a和b的最大公因数,在求解时即可直接得出x,x对应的系数大,y对应的系数小,所以要改变y,使得y 尽量为接近零的数,然后要使y变正,最后取min即可

代码:

#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
int n,a,b,x,y,k,w,ans;
inline void exgcd(int a,int b,int m,int &x,int &y)
{
	if(!b)
	{
		x=m/a;
		y=0;
	}
	else
	{
		exgcd(b,a%b,m,x,y);
		swap(x,y);
		y-=a/b*x;
	}	
}
signed main()
{
	n=read();
	a=read();
	b=read();
	k=__gcd(a,b);
	a/=k;
	b/=k;
	if(a<b)	swap(a,b);
	for(re int i=1;i<=n;++i)
	{
		w=read();
		if(w%k)
		{
			printf("-1");
			return 0;
		}
		exgcd(a,b,w/k,x,y);
		if(y<0)
		{
			x-=b*((-y)/a+1);
			y+=a*((-y)/a+1);
		}
		x+=b*(y/a);
		y-=a*(y/a);
		ans+=min(abs(x)+abs(y),abs(x+b)+abs(y-a));
	}
	printf("%lld",ans);
}

T2:数对

传送门

咕咕咕

T3:最小距离

传送门

其实是一个求伪多源最短路径,我们用一个pre数组储存其每个特殊点最近的特殊点,其实相当于染色,然后dis的意义也变为了每个特殊点离它最近的特殊点的距离,跑一遍迪杰特斯拉,再枚举每条边,若该边两端顶点不是由同一特殊点扩展得,则更改其ans;

不难证明,对于源点i,由i拓展的点j以及与j相邻且不由i拓展的点k,
如果i的最优路径从j走到了k,那么走到拓展k的源点是最优的。因此这个做
法是正确的。

正确性:对于两个特殊点的所有最短路,一定存在两个点属于不同的染色块,也就是说两个特殊点的最短路每一条都是讨论到了的

代码:

#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define N 500005
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
struct node
{
	int a,b,c;
}edge[N];
int n,m,p,x,y,z,tot;
int first[N],net[N],to[N],dis[N],w[N],pre[N],spec[N],ans[N];
bool vis[N];
priority_queue<pair<int,int> >q;
void add(int x,int y,int z)
{
	net[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
	w[tot]=z;
}
signed main()
{
	//freopen("distance.in","r",stdin);
	n=read();
	m=read();
	p=read();
	for(re int i=1;i<=p;++i)	spec[i]=read();
	for(re int i=1;i<=m;++i)
	{
		x=read(),y=read(),z=read();
		edge[i].a=x;
		edge[i].b=y;
		edge[i].c=z;
		add(x,y,z),add(y,x,z);
	}
	memset(dis,0x3f,sizeof(dis));
	for(re int i=1;i<=p;++i)	dis[spec[i]]=0,q.push(make_pair(0,spec[i])),pre[spec[i]]=spec[i];
	while(!q.empty())
	{
		int u=q.top().second;
		q.pop();
		if(vis[u])	continue;
		vis[u]=1;
		for(int e=first[u];e;e=net[e])
		{
			int v=to[e];
			if(dis[v]>dis[u]+w[e])
			{
				pre[v]=pre[u];
				dis[v]=dis[u]+w[e];
				q.push(make_pair(-dis[v],v));
			}
		}
	}
	for(re int i=1;i<=n;++i)	ans[i]=1e16;
	for(re int i=1;i<=m;++i)
	{
		int u=edge[i].a,v=edge[i].b,r=edge[i].c;
		if(pre[u]!=pre[v])
		{
			ans[pre[u]]=min(ans[pre[u]],dis[u]+r+dis[v]);
			ans[pre[v]]=min(ans[pre[v]],dis[u]+r+dis[v]);
		}
	}
	for(re int i=1;i<=p;++i)	printf("%lld ",ans[spec[i]]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值