A - 软件工程
对于一条边(i o j)
最早的开始时间有递推关系(f[j]=max{f[i]+w[i]}),正向跑一边DAG即可
最晚的开始时间有递推关系(g[i]=min{g[j]-w[i]}),反向跑一边DAG即可
#include <cstdio>
#include <iostream>
#include <climits>
using namespace std;
typedef long long ll;
const ll N=5*1e5+2,M=1e6+1;
ll n,m,a,b,c,ans,t[N],s[N],d[N],sz,sz2,vis[N],vis2[N],head[N],head2[N];
struct E{
ll next,to;
}e[M*2],e2[M*2];
void insert(ll a,ll b){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
sz2++;
e2[sz2].next=head2[b];
head2[b]=sz2;
e2[sz2].to=a;
}
void dfs(ll x){
vis[x]=1;
for (ll i=head[x];i;i=e[i].next){
ll v=e[i].to;
if (!vis[v]) dfs(v);
s[x]=max(s[x],s[v]+t[v]);
}
}
void dfs2(ll x){
vis2[x]=1;
for (ll i=head2[x];i;i=e2[i].next){
ll v=e2[i].to;
if (!vis2[v]) dfs2(v);
d[x]=min(d[x],d[v]-t[x]);
}
}
int main(){
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=n;i++) scanf("%lld",&t[i]);
for (ll i=1;i<=m;i++){
scanf("%lld%lld",&a,&b);
insert(b,a);
}
for (ll i=1;i<=n;i++) insert(i,0),insert(n+1,i),d[i]=LONG_LONG_MAX;
dfs(n+1);
t[n+1]=0,d[n+1]=s[n+1];
dfs2(0);
for (ll i=1;i<=n;i++) ans+=d[i]-s[i];
printf("%lld
%lld",s[n+1],ans);
}
B - 国际城市设计大赛
次小生成树
树上倍增,维护向上(2^i)个点的祖先(fa),以及过程中的最长边(f1)和次长边(f2),(f1)和(f2)的转移方法和吉司机线段树相同
每次尝试把一条不在MST中的边((i,j,w))加到树中,并删去原来MST中((i,j))路径上的最长边
由于要求的是严格次小生成树,所以如果最长边长度和(w)相等,就删去原来MST中((i,j))路径上的次长边
#include <cstdio>
#include <algorithm>
#include <climits>
#include <iostream>
using namespace std;
typedef long long ll;
const ll N=3*1e5+1,K=20;
ll n,m,a,b,c,sz,head[N],fa[N][K],vis[N],f1[N][K],f2[N][K],d[N],mn,mx,siz,ans=LONG_LONG_MAX;
struct P{
ll a,b,w,use;
P(){}
P(ll _a,ll _b,ll _w){a=_a,b=_b,w=_w,use=0;}
friend bool operator<(P a,P b){
return a.w<b.w;
}
}p[N];
struct E{
ll next,to,w;
}e[2*N];
void insert(ll a,ll b,ll w){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
e[sz].w=w;
}
ll find(ll x){
return fa[x][0]==x?x:fa[x][0]=find(fa[x][0]);
}
void mst(){
sort(p+1,p+m+1);
for (ll i=1;i<=n;i++) fa[i][0]=i;
for (ll i=1;i<=m;i++){
ll ta=find(p[i].a),tb=find(p[i].b);
if (ta!=tb){
p[i].use=1;
fa[tb][0]=ta;
siz+=p[i].w;
insert(p[i].a,p[i].b,p[i].w);
insert(p[i].b,p[i].a,p[i].w);
}
}
}
void dfs(ll x){
for (ll i=1;i<K;i++){
fa[x][i]=fa[fa[x][i-1]][i-1];
if (f1[x][i-1]==f1[fa[x][i-1]][i-1]){
f1[x][i]=f1[x][i-1];
f2[x][i]=max(f2[x][i-1],f2[fa[x][i-1]][i-1]);
}else if (f1[x][i-1]>f1[fa[x][i-1]][i-1]){
f1[x][i]=f1[x][i-1];
f2[x][i]=max(f2[x][i-1],f1[fa[x][i-1]][i-1]);
}else{
f1[x][i]=f1[fa[x][i-1]][i-1];
f2[x][i]=max(f1[x][i-1],f2[fa[x][i-1]][i-1]);
}
}
for (ll i=head[x];i;i=e[i].next){
ll v=e[i].to;
if (!d[v]){
d[v]=d[x]+1;
fa[v][0]=x;
f1[v][0]=e[i].w;
dfs(v);
}
}
}
void update(ll x){
if (x>mx){
mn=mx;
mx=x;
}
if (mx>x&&x>mn){
mn=x;
}
}
void lca(ll a,ll b){
mn=mx=0;
if (d[a]<d[b]) swap(a,b);
for (ll i=K-1;i>=0;i--)
if (d[fa[a][i]]>=d[b]){
update(f1[a][i]);update(f2[a][i]);
a=fa[a][i];
}
if (a==b) return;
for (ll i=K-1;i>=0;i--)
if (fa[a][i]!=fa[b][i])
{
update(f1[a][i]);update(f2[a][i]);
update(f1[b][i]);update(f2[b][i]);
a=fa[a][i],b=fa[b][i];
}
update(f1[a][0]);update(f2[a][0]);
update(f1[b][0]);update(f2[b][0]);
}
void solve(){
for (ll i=1;i<=m;i++)
if (!p[i].use){
lca(p[i].a,p[i].b);
if (p[i].w!=mx){
ans=min(ans,siz+p[i].w-mx);
}else if (mn!=0){
ans=min(ans,siz+p[i].w-mn);
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=m;i++){
scanf("%lld%lld%lld",&a,&b,&c);
p[i]=P(a,b,c);
}
mst();
d[1]=1;
fa[1][0]=0;
dfs(1);
solve();
printf("%lld",ans);
}
C - 宁王我好兄弟
D - 17 张牌你能秒我
对不等式两侧取对数,把乘法转化成加法,把不等式限制转化为最短路关系
(a-bleq x_1\b-cleq x_2\c-aleq x_3)
三个不等式相加有
(0leq x_1+x_2+x_3)
当(0>x_1+x_2+x_3)时,即图中道路存在负环,此时约束关系无解
用Bellman-Ford求最短路,如果整个图被更新了(n)次,则说明图中存在负环
#include <cstdio>
#include <cmath>
#include <cstring>
const int N=5*1e3+1;
int n,m,a,b,c,head[N],sz;
double d,f[N];
struct E{
int next,to;
double w;
}e[2*N];
void insert(int a,int b,double w){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
e[sz].w=w;
}
bool bellmanford(){
for (int i=1;i<=n;i++){
bool t=0;
for (int j=1;j<=n;j++)
for (int k=head[j];k;k=e[k].next){
int v=e[k].to;
if (f[v]>f[j]+e[k].w){
f[v]=f[j]+e[k].w;
t=1;
if (i==n) return true;
}
}
if (!t) break;
}
return false;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d%d%lf",&a,&b,&c,&d);
if (a==1) insert(b,c,-log(d));
if (a==2) insert(c,b,log(d));
if (a==3) insert(b,c,-log(d)),insert(c,b,log(d));
}
if (bellmanford())
printf("Delicious");
else
printf("DEDEDEDEDEDEDEDEDEDEDEDE");
}
E - 密室逃脱
对于每一行都建立一个源点,对于每一行都建立一个汇点
同时在容量上做限制,保证每一行每一列的流量都为(1)
矩阵中的每个点都与所在行的源点和所在列的汇点连接,设置对应的负费用
再建一个超级源点和一个超级汇点,用MCMF跑最小费用流即可,每次增广时在残存网络上跑SPFA找最短路即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <climits>
using namespace std;
const int N=102*102;
int n,sz=1,head[N],s,t,w,pre[N],dis[N],incf[N],vis[N];
queue<int> que;
struct E{
int next,to,w,c;
}e[N*4];
void insert(int a,int b,int w,int c){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
e[sz].w=w;
e[sz].c=c;
}
void flow(int a,int b,int w,int c){
insert(a,b,w,c);
insert(b,a,0,-c);
}
bool spfa(){
memset(dis,0x7f,sizeof(dis));
que.push(s);dis[s]=0;incf[s]=INT_MAX,incf[t]=0;
while(!que.empty()){
int x=que.front();que.pop();
vis[x]=0;
for (int i=head[x];i;i=e[i].next){
int v=e[i].to,w=e[i].w,c=e[i].c;
if (!w||dis[v]<=dis[x]+c) continue;
dis[v]=dis[x]+c;
incf[v]=min(w,incf[x]);
pre[v]=i;
if (!vis[v]) que.push(v),vis[v]=1;
}
}
return incf[t];
}
int maxflow,mincost;
void update(){
maxflow+=incf[t];
for (int i=t;i!=s;i=e[pre[i]^1].to){
e[pre[i]].w-=incf[t];
e[pre[i]^1].w+=incf[t];
mincost+=incf[t]*e[pre[i]].c;
}
}
int main(){
scanf("%d",&n);
s=0;t=(n+1)*(n+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
scanf("%d",&w);
flow(i,i*(n+1)+j,1,-w);
flow(i*(n+1)+j,j*(n+1),1,0);
}
for (int i=1;i<=n;i++){
flow(s,i,1,0);
flow(i*(n+1),t,1,0);
}
while(spfa()) update();
printf("%d",-mincost);
}
F - 酒厂
最小树形图,朱刘算法
#include <cstdio>
#include <cmath>
#include <cstring>
#include <climits>
#include <iostream>
using namespace std;
typedef long long ll;
const ll N=1e3+1,M=1e4+1;
ll n,m,a,b,c,siz,in[N],pos,u,v,vis[N],id[N],pre[N],sum;
struct P{
ll a,b,w;
}p[M+N];
void mdst(ll rt,ll n,ll m){
siz=0;
while(1){
for (ll i=0;i<=n;i++) in[i]=LLONG_MAX;
for (ll i=1;i<=m;i++){
ll a=p[i].a,b=p[i].b,w=p[i].w;
if (a!=b&&w<in[b]){
in[b]=w;
pre[b]=a;
if (a==rt) pos=i;
}
}
for (ll i=0;i<=n;i++){
if (i==rt) continue;
if (in[i]==LLONG_MAX){
siz=LLONG_MAX;
return;
}
}
ll cnt=0;
in[rt]=0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
for (ll i=0;i<=n;i++){
siz+=in[i];
ll b=i;
while(vis[b]!=i&&id[b]==-1&&b!=rt){
vis[b]=i;
b=pre[b];
}
if (b!=rt&&id[b]==-1){
for (ll a=pre[b];a!=b;a=pre[a])
id[a]=cnt;
id[b]=cnt++;
}
}
if (cnt==0) break;
for (ll i=0;i<=n;i++)
if (id[i]==-1) id[i]=cnt++;
for (ll i=1;i<=m;i++){
ll a=p[i].a,b=p[i].b,w=p[i].w;
p[i].a=id[a];
p[i].b=id[b];
if (id[a]!=id[b])
p[i].w-=in[b];
}
n=cnt-1;
rt=id[rt];
}
}
int main(){
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=m;i++){
scanf("%lld%lld%lld",&a,&b,&c);
p[i].a=a,p[i].b=b,p[i].w=c;
sum+=c;
}
sum++;
for (ll i=1;i<=n;i++){
p[m+i].a=0,p[m+i].b=i,p[m+i].w=sum;
}
mdst(0,n,m+n);
if (siz>=2*sum){
printf("-1");
}else{
printf("%lld %lld",siz-sum,pos-m);
}
}
G - Stardust
2-SAT
在一个期望不满足的情况下,另外两个期望必须被满足
((a,b,c))就转化为了
( eg a o b, eg a o c\ eg b o a, eg b o c\ eg c o a, eg c o b)
对于每个三元关系连六条边即可
跑一边tarjan判断是否存在(a)和( eg a)位于同一强连通分量,即存在(a)和( eg a)能够互推的情况,只有这时是不存在可行解的
#include <cstdio>
#include <iostream>
using namespace std;
const int N=2*(1e5+1);
int n,m,a,b,c,xa,xb,xc,head[N],sz,bl[N],cnt,ssz,dfn[N],vis[N],low[N],sta[N];
struct E{
int next,to;
}e[3*N];
void insert(int a,int b){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt;
vis[x]=1;
sta[++ssz]=x;
for (int i=head[x];i;i=e[i].next){
int v=e[i].to;
if (!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}else{
if (vis[v])
low[x]=min(low[x],dfn[v]);
}
}
if (dfn[x]==low[x]){
do{
bl[sta[ssz]]=x;
vis[sta[ssz]]=0;
}while(sta[ssz--]!=x);
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d%d%d%d%d",&a,&xa,&b,&xb,&c,&xc);
insert(a*2+xa^1,b*2+xb);insert(a*2+xa^1,c*2+xc);
insert(b*2+xb^1,a*2+xa);insert(b*2+xb^1,c*2+xc);
insert(c*2+xc^1,a*2+xa);insert(c*2+xc^1,b*2+xb);
}
for (int i=1;i<=2*n;i++)
if (!dfn[i])
tarjan(i);
bool t=1;
for (int i=1;i<=n;i++)
if (bl[i*2]==bl[i*2+1]) t=0;
if (t)
printf("YES");
else
printf("NO");
}
H - 快乐水偷运计划
点对((i,j))产生的贡献
(W=xotimes iotimes j)
其中(x)是比较好求,把仙人掌中的每个叶子拆开转化成树,再求树上两点间最短边即可
考虑枚举所有点对((i,j))复杂度过高,并且(xotimes i)没有很好的方法合并统计
所以考虑单从(x)入手,按照边的长度从大到小加到途中,这样新加入的边一定是连接两侧点路径上的最短边,对于两侧的点分别用并查集维护即可
由于位运算中每一位之间是独立的,所以对每一位都开一个对应的并查集,维护连通块所有点编号中(0)和(1)数量,这样就可以快速计算每次加边时带来的贡献了
#include <cstdio>
#include <climits>
#include <algorithm>
using namespace std;
typedef unsigned long long ll;
const ll N=1e5+1,M=1.5*N,K=64;
struct E{
ll next,to,w;
}e[M*2];
struct P{
ll a,b,w,incir;
P(){};
P(ll _a,ll _b,ll _w,ll _incir){a=_a,b=_b,w=_w,incir=_incir;};
friend bool operator<(P a,P b){
return a.w<b.w;
}
}p[M];
ll n,m,a,b,c,ans,esz,psz,head[N],ssz,fa[N],vis[N],cnt[N][K][2],insta[N];
P sta[M];
void cir(){
ll st=sta[ssz].b,minw=__UINT64_MAX__,minidx=0;
for (ll i=ssz;i==ssz||sta[i].b!=st;i--){
sta[i].incir=1;
if (sta[i].w<minw){
minw=sta[i].w;
minidx=i;
}
}
for (ll i=ssz;i==ssz||sta[i].b!=st;i--){
if (i!=minidx){
psz++;
p[psz]=sta[i];
p[psz].w+=minw;
}
}
}
void dfs(ll x){
vis[x]=1;
insta[x]=1;
for (ll i=head[x];i;i=e[i].next){
ll v=e[i].to;
ssz++;
sta[ssz]=P(x,v,e[i].w,0);
if (!vis[v]){
vis[v]=1;
fa[v]=x;
dfs(v);
if (!sta[ssz].incir){
psz++;
p[psz]=sta[ssz];
}
}else if (v!=fa[x]&&insta[v]){
cir();
}
ssz--;
}
insta[x]=0;
}
void insert(ll a,ll b,ll w){
esz++;
e[esz].next=head[a];
head[a]=esz;
e[esz].to=b;
e[esz].w=w;
}
ll find(ll x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void mktree(){
while(psz){
ll a=p[psz].a,b=p[psz].b,w=p[psz].w;
ll faa=find(a),fab=find(b);
for (ll i=0;i<K;i++){
for (ll j=0;j<=1;j++){
ll t=(w>>i)&1;
ans+=cnt[faa][i][j]*cnt[fab][i][j^t^1]*(1<<i);
}
}
fa[fab]=faa;
for (ll i=0;i<K;i++)
for (ll j=0;j<=1;j++)
cnt[faa][i][j]+=cnt[fab][i][j];
psz--;
}
}
int main(){
scanf("%llu%llu",&n,&m);
for (ll i=1;i<=m;i++){
scanf("%llu%llu%llu",&a,&b,&c);
insert(a,b,c);insert(b,a,c);
}
sta[0]=P(0,1,0,0);
dfs(1);
sort(p+1,p+1+psz);
for (ll i=1;i<=n;i++){
fa[i]=i;
for (ll j=0;j<K;j++)
cnt[i][j][(i>>j)&1]++;
}
mktree();
printf("%llu",ans);
}
I - LOL
Tarjan求割点
#include <cstdio>
#include <iostream>
using namespace std;
const int N=1e5+1;
int idx,n,m,a,b,sz,ans,head[N],dfn[N],low[N],cut[N],ch[N];
struct E{
int next,to;
}e[2*N];
void insert(int a,int b){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
}
void tarjan(int x){
dfn[x]=low[x]=++idx;
for (int i=head[x];i;i=e[i].next){
int v=e[i].to;
if (!dfn[v]){
tarjan(v);
if (low[v]>=dfn[x]) cut[x]=1;
low[x]=min(low[x],low[v]);
ch[x]++;
}
low[x]=min(low[x],dfn[v]);
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
insert(a,b);insert(b,a);
}
for (int i=1;i<=n;i++){
if (!dfn[i]){
tarjan(i);
cut[i]=(ch[i]>=2);
}
}
for (int i=1;i<=n;i++) ans+=cut[i];
printf("%d",ans);
}
J - 荣耀永远属于星辰十字军
素数=奇数+偶数
这样所有的边都连接的都是一个奇数和一个偶数,恰好构成了一个二分图
两部分分别对应超级源点和超级汇点,这样就可以用最大流解决了
跑一个流量就代表两点间连一条边,看能不能把所有点容量为2的边跑满即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <climits>
using namespace std;
const int N=301,M=20001;
int n,a[N],sz=1,head[M],s,t,w,pre[M],dis[M],incf[M],vis[M],pri[M],cnt[2];
queue<int> que;
struct E{
int next,to,w,c;
}e[(N*N+N)*2];
void insert(int a,int b,int w,int c){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
e[sz].w=w;
e[sz].c=c;
}
void flow(int a,int b,int w,int c){
insert(a,b,w,c);
insert(b,a,0,-c);
}
bool spfa(){
memset(dis,0x7f,sizeof(dis));
que.push(s);dis[s]=0;incf[s]=INT_MAX,incf[t]=0;
while(!que.empty()){
int x=que.front();que.pop();
vis[x]=0;
for (int i=head[x];i;i=e[i].next){
int v=e[i].to,w=e[i].w,c=e[i].c;
if (!w||dis[v]<=dis[x]+c) continue;
dis[v]=dis[x]+c;
incf[v]=min(w,incf[x]);
pre[v]=i;
if (!vis[v]) que.push(v),vis[v]=1;
}
}
return incf[t];
}
int maxflow,mincost;
void update(){
maxflow+=incf[t];
for (int i=t;i!=s;i=e[pre[i]^1].to){
e[pre[i]].w-=incf[t];
e[pre[i]^1].w+=incf[t];
mincost+=incf[t]*e[pre[i]].c;
}
}
int main(){
scanf("%d",&n);
s=0,t=1;
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
cnt[a[i]%2]++;
if (a[i]%2){
flow(a[i],1,2,0);
}else{
flow(0,a[i],2,0);
}
}
for (int i=2;i<M;i++) pri[i]=1;
for (int i=2;i<M;i++)
if (pri[i])
for (int j=2*i;j<M;j+=i) pri[j]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (a[i]%2==0&&a[j]%2==1&&pri[a[i]+a[j]]){
flow(a[i],a[j],1,0);
}
while(spfa()) update();
if (cnt[0]==cnt[1]&&maxflow==2*cnt[0])
printf("YES");
else
printf("NO");
}
K - Vingying 营救计划
L - PAFF 的演唱会
考虑到每个点的门票费,可以转化为该点到超级源点的边长
求超级源点到每个点的最短路即可
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
priority_queue<P,vector<P>,greater<P> > que;
const ll N=2*1e5+1;
ll n,m,a,b,c,sz,f[N],head[N];
struct E{
ll next,to,w;
}e[4*N];
void insert(ll a,ll b,ll w){
sz++;
e[sz].next=head[a];
head[a]=sz;
e[sz].to=b;
e[sz].w=w;
}
void dijkstra(){
memset(f,0x7f,sizeof(f));
f[0]=0;
que.push(P(0,0));
while(!que.empty()){
ll x=que.top().second;que.pop();
for (ll i=head[x];i;i=e[i].next){
ll v=e[i].to;
if(f[v]>f[x]+e[i].w){
f[v]=f[x]+e[i].w;
que.push(P(f[v],v));
}
}
}
}
int main(){
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=m;i++){
scanf("%lld%lld%lld",&a,&b,&c);
insert(a,b,c*2);insert(b,a,c*2);
}
for (ll i=1;i<=n;i++){
scanf("%lld",&a);
insert(0,i,a);insert(i,0,a);
}
dijkstra();
for (ll i=1;i<=n;i++) printf("%lld ",f[i]);
}
M - 魔法少女小糖
N - 法外狂徒
邻接矩阵快速幂,最短距离矩阵(f),方案数矩阵(g),转移时同时计算(f)和(g)
(f[i][j]=min{f_1[i][k]+f_2[k][j]})
(g[i][j]=sum_{f[i][j]=f[i][k]+f[k][j]}{g_1[i][k]*g_2[k][j]})
对于询问(x o y),最短距离为(f[x][y]),方案数为(g[x][y])
O - 间谍
P - 矩阵乘法
欧拉回路
首先用并查集判断整个图是否连通,如果不连通则无解
如果所有点的入度和出度相等,就取其中最大的数作为起点和终点
如果恰有一个点出度=入度+1,同时恰有一个点入度=出度+1,就分别把这两个点作为起点和终点
#include <cstdio>
#include <iostream>
using namespace std;
const int N=101;
int n,x,y,a[N],b[N],sig,mx,ga,gb,ans,wrong,fa[N];
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main(){
scanf("%d",&n);
for (int i=1;i<N;i++) fa[i]=i;
for (int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
a[x]++;
b[y]++;
mx=max(mx,x);
int faa=find(x),fab=find(y);
if (faa!=fab){
fa[fab]=faa;
}
}
for (int i=1;i<N;i++)
for (int j=1;j<N;j++)
if (a[i]&&a[j]&&find(i)!=find(j))
wrong=1;
for (int i=1;i<N;i++){
if (a[i]==b[i]+1) {
if (ga) wrong=1;
ga=i;
}else if (a[i]==b[i]-1) {
if (gb) wrong=1;
gb=i;
}else if (a[i]!=b[i]){
wrong=1;
}
}
if (ga&&gb){
ans=ga*gb;
}else{
ans=mx*mx;
}
if (wrong) ans=-1;
printf("%d",ans);
}
Q - 赛马 Ⅱ
把删点的操作倒着处理,就转化成了加点的操作
魔改一下Floyd最小环即可
#include <cstdio>
#include <climits>
#include <iostream>
using namespace std;
const int N=501,INF=INT_MAX/3;
int n,m,q,t[N],w[N][N],a,b,c,f[N][N],del[N],ans=INF,lans[N];
int main(){
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
w[i][j]=f[i][j]=INF;
q--;
for (int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
f[a][b]=f[b][a]=w[a][b]=w[b][a]=c;
}
for (int i=1;i<=q;i++){
scanf("%d",&a);
t[i]=a,del[a]=1;
}
for (int k=1;k<=n;k++){
if (!del[k]){
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
if (i!=j&&!del[i]&&!del[j]){
ans=min(ans,f[i][j]+w[i][k]+w[k][j]);
}
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
lans[q+1]=ans==INF?-1:ans;
for (int z=q,k=t[z];z>=1;z--,k=t[z]){
del[k]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
if (i!=j&&!del[i]&&!del[j]){
ans=min(ans,f[i][j]+w[i][k]+w[k][j]);
}
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
lans[z]=ans==INF?-1:ans;
}
for (int i=1;i<=q+1;i++) printf("%d
",lans[i]);
}