首页 > 其他分享 >Living-Dream 系列笔记 第33期

Living-Dream 系列笔记 第33期

时间:2024-03-02 17:15:18浏览次数:35  
标签:Living return fa 33 int bool using Dream cmp

Living-Dream 系列笔记强势回归(雾)

T1

并查集妙妙题。

很容易想到一种朴素的贪心策略:

对于所以物品按照价格从大到小排序,

然后每一个物品,找到最晚的没有卖物品的那一天卖掉此物品。

这样贪心的时间复杂度为 \(O(\max d_i \times n)\),可以通过。

考虑如何优化此贪心。

可以发现朴素贪心时间复杂度的瓶颈

在于找到最晚的没有卖物品的那一天,

于是我们采用并查集,

每次卖出物品时将当前这一天与上一天合并,

然后每次在第 \(x\) 天查询 \(fa_x\) 即为所求。

时间复杂度是 \(O(n \log \max d_i)\) 的。

#include<bits/stdc++.h>
using namespace std;

int n,ans,maxi;
pair<int,int> a[10031];
bool vis[10031];
int fa[10031];

bool cmp(pair<int,int> a,pair<int,int> b){
	return a.first>b.first;
}

int fnd(int x){
	return fa[x]==x?x:fa[x]=fnd(fa[x]);
}

int main(){
	while(cin>>n){
		ans=0,maxi=-1e9;
		for(int i=1;i<=n;i++) 
			cin>>a[i].first>>a[i].second,maxi=max(maxi,a[i].second);
		sort(a+1,a+n+1,cmp);
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=maxi;i++) fa[i]=i;
		for(int i=1;i<=n;i++){
			int pos=fnd(a[i].second);
			if(pos>0)
				ans+=a[i].first,fa[pos]=pos-1;
		}
		cout<<ans<<'\n';
	}
	return 0;
}

似乎还有一种堆做法,有心情再更。

upd:堆做法在本人的算法竞赛进阶指南笔记中有。

T2

最小生成树的板子。

最小生成树的定义:\(n\) 点 \(m\) 边的无向连通图中边权和最小的 \(n\) 点树。

如何求最小生成树:

  • \(\texttt{Kruskal}\)

  • \(\texttt{Prim}\)(暂未学习)

\(\texttt{Kruskal}\) 算法:

将 \(m\) 边按照边权从小到大排序,

然后对于两个节点,

若它们未联通(用并查集维护),

就对它们进行连边即可。

证明:(询问)

#include<bits/stdc++.h>
using namespace std;

int n,m;
int fa[200031];
struct E{
	int u,v,w;
}e[200031];
bool cmp(E x,E y){
	return x.w<y.w;
}

int fnd(int x){
	return fa[x]==x?x:fa[x]=fnd(fa[x]);
}
void mrg(int x,int y){
	x=fnd(x),y=fnd(y);
	fa[x]=y;
} 

int kruskal(){
	int ans=0,cnt=0;
	for(int i=1;i<=m;i++){
		if(fnd(e[i].u)!=fnd(e[i].v)){
			mrg(e[i].u,e[i].v);
			ans+=e[i].w;
			cnt++;
			if(cnt==n-1) return ans;
		}
	}
	return -1;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w;
	sort(e+1,e+m+1,cmp);
	int Ans=kruskal();
	if(Ans==-1) cout<<"orz";
	else cout<<Ans;
	return 0;
}

T3

大水题。

将答案累加改为答案取 \(\max\) 即可。

#include<bits/stdc++.h>
using namespace std;

int n,m;
int fa[100031];
struct E{
	int u,v,w;
}e[100031];
bool cmp(E x,E y){
	return x.w<y.w;
}

int fnd(int x){
	return fa[x]==x?x:fa[x]=fnd(fa[x]);
}
void mrg(int x,int y){
	x=fnd(x),y=fnd(y);
	fa[x]=y;
} 

int kruskal(){
	int ans=-1e9,cnt=0;
	for(int i=1;i<=m;i++){
		if(fnd(e[i].u)!=fnd(e[i].v)){
			mrg(e[i].u,e[i].v);
			ans=max(ans,e[i].w);
			cnt++;
			if(cnt==n-1) return ans;
		}
	}
	return -1;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w;
	sort(e+1,e+m+1,cmp);
	//if(Ans==-1) cout<<"orz";
	cout<<kruskal();
	return 0;
}

T4

大水题 \(\times \ 2\)。

终止条件改为连通块数量为 \(k\) 即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,k;
int fa[200031];
struct E{
	int u,v,w;
}e[200031];
bool cmp(E x,E y){
	return x.w<y.w;
}

int fnd(int x){
	return fa[x]==x?x:fa[x]=fnd(fa[x]);
}
void mrg(int x,int y){
	x=fnd(x),y=fnd(y);
	fa[x]=y;
} 

bool check(){
	int cnt=0;
	for(int i=1;i<=n;i++)
		if(fa[i]==i) cnt++;
	return cnt==k;
}

int kruskal(){
	int ans=0;
	for(int i=1;i<=m;i++){
		if(fnd(e[i].u)!=fnd(e[i].v)){
			mrg(e[i].u,e[i].v);
			ans+=e[i].w;
			if(check()) return ans;
		}
	}
	return -1;
}

int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v>>e[i].w;
	sort(e+1,e+m+1,cmp);
	int Ans=kruskal();
	if(Ans==-1) cout<<"No Answer";
	else cout<<Ans;
	return 0;
}

习题 T1

大水题 \(\times \ 3\)。

将边权改为距离,端点改为下标,

终止条件改为 \(cnt=(s-1)-(p-1)=s-p\) 即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,tot;
map<int,int> fa;
int x[531],y[531];
struct E{
	int u,v;
	double w;
}e[250031];
bool cmp(E x,E y){
	return x.w<y.w;
}
double get_dis(int x,int y,int xx,int yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
int c(int x){
	int y=0;
	while(x) y++,x/=10;
	return y;
}

int fnd(int x){
	return fa[x]==x?x:fa[x]=fnd(fa[x]);
}
void mrg(int x,int y){
	x=fnd(x),y=fnd(y);
	fa[x]=y;
}

double kruskal(){
	double ans=0;
	int cnt=0;
	for(int i=1;i<=tot;i++){
		if(fnd(e[i].u)!=fnd(e[i].v)){
			mrg(e[i].u,e[i].v);
			ans=e[i].w;
			cnt++;
			if(cnt==n-m) return ans;
		}
	}
}

int main(){
	cin>>m>>n;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
	for(int i=1;i<=n;i++)
		for(int j=1;j<i;j++)
			e[++tot].w=get_dis(x[i],y[i],x[j],y[j]),
			e[tot].u=i,
			e[tot].v=j;
	sort(e+1,e+tot+1,cmp);
	double Ans=kruskal();
	cout<<setprecision(2)<<fixed<<Ans;
	return 0;
}

标签:Living,return,fa,33,int,bool,using,Dream,cmp
From: https://www.cnblogs.com/XOF-0-0/p/18048887

相关文章

  • Living-Dream 系列笔记 第38期
    T1floyd模板。#include<bits/stdc++.h>usingnamespacestd;intn,m;intdp[131][131];voidfloyd(){ for(intk=1;k<=n;k++) for(inti=1;i<=n;i++) for(intj=1;j<=n;j++) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);}intmain(){ cin&g......
  • Living-Dream 系列笔记 第46期
    强连通分量(StronglyConnectedComponents,SCC)。强连通:有向图中,\(x,y\)能相互到达。弱连通:有向图中,\(x\)能到\(y\),\(y\)不能到\(x\)(或反之)。强连通分量:有向图\(G\)中一极大子图\(G1\),使得\(G1\)任意两点均强连通,且\(G1\)不可变得更大(不能添加点)。强连通分量要么是......
  • Living-Dream 系列笔记 第43期
    bellman-ford:因为最短路最多\(n\)点\(n-1\)边,则进行\(n-1\)轮操作,每轮枚举\(m\)边进行松弛即可。时间复杂度\(O(nm)\)。spfa:正确的称呼是队列优化的bellman-ford。我们知道,对于一个点,只有它被松弛了,它的邻接点才有可能被松弛。于是我们用队列记录可能被松弛的点,每......
  • Living-Dream 系列笔记 第45期
    负环,即负权环,指在图\(G\)中边权和为负数的一回路。负环的判定一般有两种方式。以下均以\(n\)点\(m\)边的图\(G\)为例。法一:以边为研究对象。注意到最短路边数一定不超过\(n-1\)边,因此维护\(cnt_x\)表示起点到\(x\)的边数,若某一时刻存在\(cnt_x>n-1\)则存在......
  • Living-Dream 系列笔记 第42期
    T1枚举流量对于花费跑dijkstra并取比值的\(\max\)即可。关于为什么枚举流量不一定当前最优的问题,因为最优解的流量总在枚举范围内,所以无需考虑当前是否最优。#include<bits/stdc++.h>usingnamespacestd;intn,m,ans;intdis[100031];boolvis[100031];structEdge{......
  • Living-Dream 系列笔记 第44期
    比赛链接T1\(100\to10\)。错因:01背包转移方程写错,没有取\(\max\)。并查集合并错写成将u,v而非fnd(u),fnd(v)合并。#include<bits/stdc++.h>usingnamespacestd;intn,m,w;intc[10031],d[10031];intfa[10031];intdp[10031];intfnd(intx){return......
  • Living-Dream 系列笔记 第41期
    稍微讲一下dijkstraqwq。dijkstra、bellman-ford(orspfa)、bfs的区别:dijkstra以点为研究对象;bellman-ford/spfa则以边为研究对象;而bfs可以看作边权为\(1\)(or全都相同)的dijkstra,因此它可以用队列实现。dijkstra的具体实现:将所有点分为两类(黑点/白点),......
  • Living-Dream 系列笔记 第40期
    T1bf的做法是\(n\)次floyd,实测可以卡过。然后我们发现当点\(u\)为重要点时,当且仅当存在\((a,b)\)使得\(u\)为它们的唯一中转点。于是我们令\(vis_{i,j}\)表示\((i,j)\)的唯一中转点,接着在floyd的松弛操作中若能松弛则更新其为当前中转点\(k\),否则若没有更优......
  • Living-Dream 系列笔记 第39期
    T1一头牛能确定关系,当且仅当它能到达/被到达除自己外的所有牛。于是我们建出有向图,跑floyd传递闭包,然后对于每个点统计他能到达的点数是否为\(n-1\)即可。#include<bits/stdc++.h>usingnamespacestd;intn,m,ans;intdp[131][131];vector<int>G[4531];voidfl......
  • Living-Dream 周考总结 第3期
    Link,第四场没打。T1\(100\),没挂分。\(\operatorname{lcm}(x,y)=\dfrac{x}{\gcd(x,y)}\timesy\)#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;signedmain(){ //freopen("as01.in","r",stdin); //freopen("as0......