今天又差点垫底,下次要加油啊。
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,y−kgcd(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;
}