最近做题小结
https://www.luogu.com.cn/problem/AT_abc375_e
观察数据范围 发现数据还是很小的 明显是背包类的DP 直接动手思考DP转移
第一维开N 第二维开什么好呢 注意到所有的b加起来才1500
评价没人才500 于是我们就知道了
DP N 500 500 500
但是这样会炸 mle 其实最后一个500 可以省略
还是挺有意思的 不是黄的题目 考虑的细节蛮多的 滚动数组也不行
https://www.luogu.com.cn/problem/AT_abc375_f
这题还是很有意思的
还是倒着做的思想 我们把删边的思想转化为增加边的思想
然后对于一个边有没有作用 这已经是人尽皆知的操作了
比如U-V的边
有两个点X Y x到y的
dis x y =min dis x u+dis v y 或者 dis x v +dis u y 或者dis x y
只有这样了
所以我们只需要把删除边的剩下边 跑一次最短路 然后再倒着做 把边不断增加不断对他进行取min就好了 这样xy需要for循环枚举
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int range = 3e3 + 100;
int n, m, a, b, c;
int dis[range][range];
void floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
int q;
pair<pair<int, int>,int>p1[range * 1000];
bool vis[range*1000];
pair<int,pair<int,int>>pp[range*1000];
vector<int>ans;
signed main() {
cin >> n >> m >> q;
// memset(dis,0x3f3f3f3f, sizeof dis);
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)dis[i][j]=1e17;
}
int st=dis[0][0];
// cout<<st<<endl;
for (int i = 1; i <= n; i++)dis[i][i] = 0;
for (int i = 1; i <= m; i++) {
int x,y,z;
cin>>x>>y>>z;
p1[i].first={x,y};p1[i].second={z};
}
for (int i = 1; i <= q; i++) {
int op, a, b;
cin >> op ;
if(op==1)
{
cin>>a,vis[a]=1;
pp[i].first=1;
pp[i].second={a,0};
}
else {
cin>>a>>b;
pp[i].first=2;
pp[i].second={a,b};
}
}
for(int i=1;i<=m;i++)
{
if(vis[i])continue;
int a=p1[i].first.first;
int b=p1[i].first.second;
int c=p1[i].second;
dis[a][b] = min(dis[a][b], c); //重边
dis[b][a] = min(dis[b][a], c); //重边
}
floyd();
for(int i=q;i>=1;i--)
{
//模拟增边
if(pp[i].first==1)
{
int bianhao=pp[i].second.first;
int a=p1[bianhao].first.first;
int b=p1[bianhao].first.second;
int c=p1[bianhao].second;
dis[a][b] = min(dis[a][b], c); //重边
dis[b][a] = min(dis[b][a], c); //重边
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], min(dis[i][a] + dis[b][j]+dis[a][b],dis[i][b]+dis[a][j]+dis[b][a]));
}
else{
int x,y;
x=pp[i].second.first;
y=pp[i].second.second;
if(dis[x][y]!=st)
ans.push_back(dis[x][y]);
// else cout<<-1<<endl;
else ans.push_back(-1);
}
}
reverse(ans.begin(),ans.end());
for(auto i:ans)
{
cout<<i<<endl;
}
return 0;
}
https://www.luogu.com.cn/problem/AT_abc375_g
、
非常好的一道题 做不来 讲下正解思路吧
因为如果是最短路的边 则一定需要满足
首先 dis 1 n = dis 1 u+dis n v ||
dis 1 n =dis1 v +dis n u
这个是必须的 只是第一个要求
然后介绍两种写法
第一种根据性质来写
这个我没试过
下面这个我试了 我们可以想到既然是独一无二的边 那肯定少了他不行
少了他不行的边是什么边 很明显 是割边
所以我们首先 找出割边 然后我们再提前预处理满足
dis 1 n = dis 1 u+dis n v ||
dis 1 n =dis1 v +dis n u
这个性质的边 然后最后判断就行
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int range=3e5;
struct node{
int v;
int w;
};
vector<node>e[range];
struct bian{
int x,y,z;
}b[range];
vector<int>E[range];
int dfn[range],low[range],id=0,s=0;
map<pair<int,int>,bool>ma;
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++id;
for(auto i:E[x])
{
if(!dfn[i])
{
tarjan(i,x);// 6 5 //5 5 XD LOW [6]>SK[5]++
//2 -4 -1 LOW[]
low[x]=min(low[x],low[i]);
if(low[i]>dfn[x])
ma[{x,i}]=1,ma[{i,x}]=1;
// vec[x].push_back(i);//模拟两个点的树就可以懂了
//只要是割边 肯定会到这一步来 就是两个点互相比较
//所以i!=fa很重要 因为到了割边 就是搜索到了最后一个时i
//i==fa的
}
else if(i!=fa) low[x]=min(low[x],dfn[i]);
}
}
struct Node{
int u;
int dis;
friend bool operator<(Node x,Node y)
{
return x.dis>y.dis;
}
};
bool vis[range];
bool viss[range];
int dis[range];
int diss[range];
void dijkstra2(int s)
{
priority_queue<Node>q;
memset(diss,0x3f,sizeof dis);
q.push({s,0});
diss[s]=0;
while(q.size())
{
int u=q.top().u;
q.pop();
if(viss[u])continue;
viss[u]=1;
for(auto i:e[u])
{
int v=i.v;
int w=i.w;
if(diss[v]>diss[u]+w)
{
diss[v]=diss[u]+w;
q.push({v,diss[v]});
}
}
}
}
void dijkstra1(int s)
{
priority_queue<Node>q;
memset(dis,0x3f,sizeof dis);
q.push({s,0});
dis[s]=0;
while(q.size())
{
int u=q.top().u;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(auto i:e[u])
{
int v=i.v;
int w=i.w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({v,dis[v]});
}
}
}
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
b[i]={x,y,z};
e[x].push_back({y,z});
e[y].push_back({x,z});
}
dijkstra1(1);
// int pos1=dis[n];
dijkstra2(n);
// int pos2=diss[1];
for(int i=1;i<=m;i++)
{
int x,y,z;
x=b[i].x;
y=b[i].y;z=b[i].z;
if(dis[x]+diss[y]+z==dis[n]||dis[y]+diss[x]+z==dis[n])
{
E[x].push_back(y);E[y].push_back(x);
}
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) tarjan(i,-1);
}
for(int i=1;i<=m;i++)
{
int x=b[i].x;
int y=b[i].y;
if(ma[{x,y}]||ma[{y,x}])
{
cout<<"Yes"<<endl;
}
else cout<<"No"<<endl;
}
}
然后再把第一种做法的代码给下
给核心代码
if(dis[a[i].y]>=dis[u]+a[i].z)
{
if(dis[a[i].y]==dis[u]+a[i].z)posi[a[i].y]=(posi[a[i].y]+posi[u])%mod;//距离相等则方案数累加
else
{
posi[a[i].y]=posi[u];//距离更短则方案数为先前结点的方案数
dis[a[i].y]=dis[u]+a[i].z;
q.push({a[i].y,dis[a[i].y]});
}
}
这个是pos的保存 很重要
最后的判断
for(int i=1;i<=idx;i+=2)
{
if(dis[a[i].x]+dis2[a[i].y]+a[i].z!=dis[ed]&&dis[a[i].y]+dis2[a[i].x]+a[i].z!=dis[ed])cout<<"No\n";//距离不相等
else
{
if(((dis[a[i].x]+dis2[a[i].y]+a[i].z==dis[ed])&&(posi[a[i].x]*posi2[a[i].y]%mod==posi[ed]))||((dis[a[i].y]+dis2[a[i].x]+a[i].z==dis[ed])&&(posi[a[i].y]*posi2[a[i].x])%mod==posi[ed]))cout<<"Yes\n";
else cout<<"No\n";
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】