最近做题小结

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";
		}
	}
posted @   想念不动声色  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示