2.图论

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

开题顺序: \(DQEABC\)

\(A\) [ABC077D] Small Multiple

  • \(f_{x}\) 表示 \(\mod k=x\) 的数中的最小数位和,状态转移方程为 \(f_{(10x+y) \bmod k}=\min(f_{(10x+y) \bmod k},f_{x}+y)\) ,考虑使用 \(Dijsktra\) 辅助转移。

  • 具体实现时新建一个虚点 \(k\) 分别向 \(i \in [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\) 没卡过去 ,只能另寻他法。

  • 考虑优化状态设计中的 \(x_{l_{i}-1}-x_{r_{i}} \le -c_{i}\)

  • \(x_{i}\) 表示 \([1,i]\)\(0\) 的个数,则有 \(\begin{cases} x_{r_{i}}-x_{l_{i}-1} \le r-l+1-c_{i} \\ 0 \le x_{i}-x_{i-1} \le 1 \end{cases}\) ,然后跑 \(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\) 数组的定义,容易发现 \(dis_{i}-x\) 一定不能拼出来,故 \(\max\limits_{i=0}^{x-1}\{ dis_{i}-x \}\) 即为所求。

  • 暴力建边即可。

    点击查看代码
    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 西瓜种植

  • \(x_{i}\) 表示 \([1,i]\) 中选择的数的个数。

  • \([a_{i},b_{i}]\) 中至少有 \(c_{i}\) 个数被选出等价于 \(x_{b_{i}}-x_{a_{i}-1} \ge c_{i}\)

  • 由于一个数只能选一次,隐含着 \(0 \le x_{i}-x_{i-1} \le 1\) 的关系。

    点击查看代码
    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 映射后跑 \(2-SAT\) 即可。

    点击查看代码
    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)\) 覆盖(不妨钦定 \(\begin{cases} dep_{u}>dep_{v} \\ dep_{x}>dep_{y} \\ dep_{v}<dep_{y} \end{cases}\) )了,画图简单分讨后得到 \(\begin{cases} y \to \operatorname{LCA}(u,x) \\ y \to v \to u \to \operatorname{LCA}(u,x) \\ y \to x \to \operatorname{LCA}(u,x) \end{cases}\) 即为一组可行解。

    点击查看代码
    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\) 根柱子的影响为 \(h_{i,j}=\min(h_{i-1,j}-1,h_{i-1,j-1},h_{i-1,j+1})\)

  • \(f_{i}\) 表示第 \(i\) 根柱子被摧毁的时间,类似地,状态转移方程为 \(f_{i}=\min(f_{i-1}+1,h_{i},f_{i+1}+1)\)

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

  • 最终有 \(\max\limits_{i=1}^{n} \{ f_{i} \}\) 即为所求。

    点击查看代码
    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 @ 2024-10-05 07:12  hzoi_Shadow  阅读(39)  评论(1编辑  收藏  举报
扩大
缩小