2.图论

图论1(最短路,最小生成树,差分约束,LCA)

开题顺序: DQEABC

A [ABC077D] Small Multiple

  • fx 表示 modk=x 的数中的最小数位和,状态转移方程为 f(10x+y)modk=min(f(10x+y)modk,fx+y) ,考虑使用 Dijsktra 辅助转移。

  • 具体实现时新建一个虚点 k 分别向 i[1,9] 连一条权值为 i 的有向边。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[1100010];
    ll head[1100010],dis[1100010],vis[1100010],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra(ll s)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<pair<ll,ll> >q;
    	dis[s]=0;
    	q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		ll x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll k,i,j;
    	cin>>k;
    	for(i=0;i<=k-1;i++)
    	{
    		for(j=0;j<=9;j++)
    		{
    			add(i,(i*10+j)%k,j);
    		}
    	}
    	for(i=1;i<=9;i++)
    	{
    		add(k,i%k,i);
    	}
    	dijkstra(k);
    	cout<<dis[0]<<endl;
    	return 0;
    }
    

B [ABC216G] 01Sequence

  • deque 优化 SPFA 没卡过去 ,只能另寻他法。

  • 考虑优化状态设计中的 xli1xrici

  • xi 表示 [1,i]0 的个数,则有 {xrixli1rl+1ci0xixi11 ,然后跑 Dijsktra 求最短路即可。

  • 最后再将答案取反即可。

    点击查看代码
    struct node
    {
    	int nxt,to,w;
    }e[600010];
    int head[600010],dis[600010],vis[600010],cnt=0;
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra(int s)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<pair<int,int> >q;
    	dis[s]=0;
    	q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		int x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);	
    #endif
    	int n,m,u,v,w,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		add(u-1,v,v-u+1-w);
    	}
    	for(i=0;i<=n;i++)
    	{
    		add(i,i+1,1);
    		add(i+1,i,0);
    	}
    	dijkstra(0);
    	for(i=1;i<=n;i++)
    	{
    		cout<<((dis[i]-dis[i-1])^1)<<" ";
    	}
    	return 0;
    }
    

C luogu P2662 牛场围栏

  • 貌似从 luogu P3951 [NOIP2017 提高组] 小凯的疑惑 / [蓝桥杯 2013 省] 买不到的数目 直接扩展不来。

  • 挖掘同余最短路中 dis 数组的定义,容易发现 disix 一定不能拼出来,故 maxi=0x1{disix} 即为所求。

  • 暴力建边即可。

    点击查看代码
    struct node
    {
    	int nxt,to,w;
    }e[300010];
    int head[300010],dis[300010],vis[300010],a[300010],cnt=0;
    set<int>s;
    set<int>::iterator it;
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra(int s)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<pair<int,int> >q;
    	dis[s]=0;
    	q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		int x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    			vis[x]=1;
    		{
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,ans=0,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		for(j=a[i];j>=0&&j>=a[i]-m;j--)
    		{
    			s.insert(j);
    		}
    	}
    	for(it=s.begin();it!=s.end();it++)
    	{
    		if(it!=s.begin())
    		{
    			for(j=0;j<=*s.begin()-1;j++)
    			{
    				add(j,(j+*it)%*s.begin(),*it);
    			}
    		}
    	}
    	dijkstra(0);
    	for(i=0;i<=*s.begin()-1;i++)
    	{
    		if(dis[i]==0x3f3f3f3f)
    		{
    			ans=-1;
    			break; 
    		}
    		ans=max(ans,dis[i]-*s.begin());
    	}
    	cout<<(ans==0?-1:ans)<<endl;
    	return 0;
    }
    

D luogu P2680 [NOIP2015 提高组] 运输计划

E luogu P4211 [LNOI2014] LCA

F luogu P1852 跳跳棋

G luogu P10652 「ROI 2017 Day 1」前往大都会

H CF1005F Berland and the Shortest Paths

I CF507E Breaking Good

J CF1981E Turtle and Intersected Segments

K CF125E MST Company

L CF1707C DFS Trees

M CF1051F The Shortest Statement

N 「JOISC 2015 Day 1」卡片占卜

O [AGC016D] XOR Replace

P [AGC004D] Teleporter

Q SP116 INTERVAL - Intervals

  • 多倍经验: luogu P1645 序列 | luogu P1250 种树 | luogu P1986 元旦晚会 | UVA1723 Intervals | luogu P10934 西瓜种植

  • xi 表示 [1,i] 中选择的数的个数。

  • [ai,bi] 中至少有 ci 个数被选出等价于 xbixai1ci

  • 由于一个数只能选一次,隐含着 0xixi11 的关系。

    点击查看代码
    struct node
    {
    	int nxt,to,w;
    }e[500010];
    int head[500010],vis[500010],dis[500010],cnt=0;
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void spfa(int s)
    {
    	memset(vis,0,sizeof(vis));
    	memset(dis,-0x3f,sizeof(dis));
    	queue<int>q;
    	q.push(s);
    	dis[s]=0;
    	vis[s]=1;
    	while(q.empty()==0)
    	{
    		int x=q.front();
    		vis[x]=0;
    		q.pop();
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(dis[e[i].to]<dis[x]+e[i].w)
    			{
    				dis[e[i].to]=dis[x]+e[i].w;
    				if(vis[e[i].to]==0)
    				{
    					q.push(e[i].to);
    					vis[e[i].to]=1;
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	int n,r,u,v,w,i,t,j;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		cin>>n;
    		r=cnt=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		for(i=1;i<=n;i++)
    		{
    			cin>>u>>v>>w;
    			add(u-1,v,w);
    			r=max(r,v);
    		}
    		for(i=0;i<=r;i++)
    		{
    			add(i,i+1,0);
    			add(i+1,i,-1);
    		}
    		spfa(0);
    		cout<<dis[r]<<endl;
    		if(j!=t)
    		{
    			cout<<endl;
    		}
    	}
    	return 0;
    }
    

图论2(tarjan,2-SAT,欧拉路,竞赛图,图的性质,综合应用)

开题顺序: ABFDC

A luogu P8435 【模板】点双连通分量

B luogu P7771 【模板】欧拉路径

  • 等有时间来补学习笔记。

C luogu P4171 [JSOI2010] 满汉全席

  • m,h 映射后跑 2SAT 即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[2010];
    int head[210],dfn[210],low[210],ins[210],col[210],tot=0,scc_cnt=0,cnt=0;
    stack<int>s;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void tarjan(int x)
    {
    	tot++;
    	dfn[x]=low[x]=tot;
    	ins[x]=1;
    	s.push(x);
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(dfn[e[i].to]==0)
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    		}
    		else
    		{
    			if(ins[e[i].to]==1)
    			{
    				low[x]=min(low[x],dfn[e[i].to]);
    			}
    		}
    	}
    	if(dfn[x]==low[x])
    	{
    		scc_cnt++;
    		int tmp=0;
    		while(x!=tmp)
    		{
    			tmp=s.top();
    			s.pop();
    			ins[tmp]=0;
    			col[tmp]=scc_cnt;
    		}
    	}
    }
    bool check(int n)
    {
    	for(int i=1;i<=n;i++)
    	{
    		if(col[i]==col[i+n])
    		{
    			return false;
    		}
    	}
    	return true;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,m,x,y,i,j;
    	char a,b;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		cnt=tot=scc_cnt=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(dfn,0,sizeof(dfn));
    		cin>>n>>m;
    		for(i=1;i<=m;i++)
    		{
    			cin>>a>>x>>b>>y;
    			add(x+(1-(a=='h'))*n,y+(b=='h')*n);
    			add(y+(1-(b=='h'))*n,x+(a=='h')*n);
    		}
    		for(i=1;i<=2*n;i++)
    		{
    			if(dfn[i]==0)
    			{
    				tarjan(i);
    			}
    		}
    		cout<<(check(n)==true?"GOOD":"BAD")<<endl;
    	}
    	return 0;
    }
    

D CF521E Cycling City

  • 多倍经验: luogu P7025 [NWRRC2017] Grand Test

  • 有解当且仅当存在两个环的交集不为空集

  • DFS 树上每条非树边都对应原图上一个环,此时可以转化为判断是否有一条树边由两条非树边构成的环覆盖。

  • 树上差分难以输出路径,考虑暴力跳非树边进行覆盖并在覆盖次数 =2 时及时退出。

  • 输出路径时,设覆盖 (u,v) 时发现已经被 (x,y) 覆盖(不妨钦定 {depu>depvdepx>depydepv<depy )了,画图简单分讨后得到 {yLCA(u,x)yvuLCA(u,x)yxLCA(u,x) 即为一组可行解。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[400010];
    int head[400010],fa[400010],dep[400010],vis[400010],ins[400010],cnt=0;
    pair<int,int>col[400010];
    deque<int>ans;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    int lca(int x,int y)
    {
    	if(dep[x]<dep[y])
    	{
    		swap(x,y);
    	}
    	while(dep[x]>dep[y])
    	{
    		x=fa[x];
    	}
    	while(x!=y)
    	{
    		x=fa[x];
    		y=fa[y];
    	}
    	return x;
    }
    void up(int x,int y)
    {
    	for(;x!=y;x=fa[x])
    	{
    		ans.push_front(x);
    	}
    	ans.push_front(y);
    }
    void print(int u,int v,int x,int y)
    {
    	if(dep[v]>dep[y])
    	{
    		swap(u,x);
    		swap(v,y);
    	}
    	cout<<"YES"<<endl;
    	int rt=lca(u,x);
    	up(rt,y);
    	cout<<ans.size()<<" ";
    	while(ans.empty()==0)
    	{
    		cout<<ans.front()<<" ";
    		ans.pop_front();
    	}
    	cout<<endl;
    	up(y,v);
    	up(u,rt);
    	cout<<ans.size()<<" ";
    	while(ans.empty()==0)
    	{
    		cout<<ans.back()<<" ";
    		ans.pop_back();
    	}
    	cout<<endl;
    	ans.push_back(y);
    	up(x,rt);
    	cout<<ans.size()<<" ";
    	while(ans.empty()==0)
    	{
    		cout<<ans.back()<<" ";
    		ans.pop_back();
    	}
    	cout<<endl;
    	exit(0);
    }
    void cover(int x,int y)
    {
    	for(int rt=x;rt!=y;rt=fa[rt])
    	{
    		if(col[rt].first!=0)
    		{
    			print(col[rt].first,col[rt].second,x,y);
    		}
    		else
    		{
    			col[rt]=make_pair(x,y);
    		}
    	}
    }
    void dfs(int x,int father)
    {
    	vis[x]=ins[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father)
    		{
    			if(vis[e[i].to]==0)
    			{
    				dfs(e[i].to,x);
    			}
    			else
    			{
    				if(ins[e[i].to]==1)
    				{
    					cover(x,e[i].to);
    				}
    			}
    		}
    	}
    	ins[x]=0;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{	
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(vis[i]==0)
    		{
    			dfs(i,0);
    		}
    	}
    	cout<<"NO"<<endl;
    	return 0;
    }
    

E CF1916F Group Division

F CF573B Bear and Blocks

  • 观察到第 i 次操作对第 j 根柱子的影响为 hi,j=min(hi1,j1,hi1,j1,hi1,j+1)

  • fi 表示第 i 根柱子被摧毁的时间,类似地,状态转移方程为 fi=min(fi1+1,hi,fi+1+1)

  • 转移过程实际上是分别以 0,n+1 为起点跑最短路,所以正反各更新一遍即可。

  • 最终有 maxi=1n{fi} 即为所求。

    点击查看代码
    ll h[100010],f[100010];
    int main()
    {
    	ll n,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>h[i];
    		f[i]=min(f[i-1]+1,h[i]);
    	}
    	for(i=n;i>=1;i--)
    	{
    		f[i]=min(f[i+1]+1,f[i]);
    	}
    	for(i=1;i<=n;i++)
    	{
    		ans=max(ans,f[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

G CF1779E Anya's Simultaneous Exhibition

H CF1864G Magic Square

I CF1819E Roads in E City

J CF1919F2 Wine Factory (Hard Version)

图论3(二分图、网络流等)

A luogu P4382 [八省联考 2018] 劈配

B luogu P2469 [SDOI2010] 星际竞速

C luogu P9167 [省选联考 2023] 城市建造

D CF708D Incorrect Flow

E CF1250K Projectors

F luogu P2050 [NOI2012] 美食节

G CF1163F Indecisive Taxi Fee

H CF1773J Jumbled Trees

I UOJ 461. 新年的Dog划分

J luogu P10062 [SNOI2024] 拉丁方

K UOJ 592. 新年的聚会

L luogu P7214 [JOISC2020] 治療計画

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