191012CSP模拟

本文介绍了作者在191012CSP模拟考试中的经历,重点分析了三道题目:困难的图论问题涉及无向连通图的简单环查找,使用树上差分和双连通分量;中等难度的字符串问题讨论了利用AC自动机和矩阵加速求解最大得分;简单的上网问题涉及线段树和拓扑排序解决士兵防守值的区间查询。文章分享了不同解题方法和思路,并提供了相关代码链接。
摘要由CSDN通过智能技术生成

今天考试的三道题的难度都只达到NOIP T2或者超过一点,可我才骗到了10分,实属能力不够,愿下次改进;
这次考试,我不足的知识点有:树上差分,求双连通分量,AC自动机,矩阵,差分约束系统,拓扑排序,线段树优化建图;

T1:困难的图论

给定由n 个点m 条边组成的无向连通图,保证没有重边和自环。
你需要找出所有边,满足这些边恰好存在于一个简单环中。一个环被称为简单环,当且仅当它包含的所有点都只在这个环中被经过了一次。注意到这些边可能有很多条,你只需要输出他们编号的异或和即可。

法一:因为是无向图,所以非树边都是返祖边,则可得:若一条边在一个简单环中

•.返祖边,且它覆盖的树边不和任何其他返祖边覆盖的树边有公共边。
•树边且被一条返祖边覆盖,覆盖它的返祖边覆盖的树边不和其他返祖边覆盖的树边有公共边。

可以利用树上差分的思想计算被多少返祖边覆盖,计算是否有重合相当于链上查询,也可以利用差分做到;

法二:简单环也是环,则考虑求出所有点双连通分量,首先这个点双连通分量里的边要合法的话,那么内部的边数一定大等于点数(因为有环)。其次这个点双连通分量里如果边数大于点数,那么所有边可以由两个不同的环覆盖,所以在这个点双连通分量内只能点数等于边数;

我们只需求出所有的点双连通分量,然后把那些点数等于边数的点双连通分量内部的边标记为合法即可,利用Tarjan 算法可以做到O( n+m)

教练的std用的stack和int,然而在OJ上提交时会runtime error,TMD必须手写栈,而且开long long,数组大小还要控制死,不然就是MIT,真的是一道毒瘤题。

法一代码请见:https://blog.csdn.net/sslz_fsy/article/details/102518415
来自FSY巨神
树上差分思想优化https://www.sohu.com/a/271430685_100201031

代码(法二)

#include<bits/stdc++.h>
#define  int  long long//TMD用int是runtime error 
#define N 1000005
using namespace std;
inline int read()
{
   
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
   
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
   
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
int dfn[N],low[N],qq[N],q[N],top1,top2;//qq存点,q存边 
int bcc,sc[N],n,m,x,y,tot=1,times,ans;
int first[N],net[2*N],to[2*N];
//stack <int>qq;
//stack <int>q; 还不能手写栈 
vector<int>a[N];//存点双连通分量含的边 
bool used[N],vis[N];
inline void add(int x,int y)
{
   
	net[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
inline void targin(int x)
{
   
	dfn[x]=low[x]=++times;
	qq[++top1]=x;
	for(int i=first[x];i;i=net[i])
	{
   
		int y=to[i];
		if(used[i>>1])	continue;
		used[i>>1]=1;
		q[++top2]=i>>1;///
		if(!dfn[y])
		{
   
			targin(y);
			low[x]=min(low[x],low[y]);
			if(low[y]<dfn[x])	continue;//判断是否是点双联通分量 
			bcc++;
			while(1)
			{
   
				int z=qq[top1--];
				sc[bcc]++;
				if(z==y)	break;
			}
			sc[bcc]++;/要多加一次 
			while(1)
			{
   
				int z=q[top2--];
				a[bcc].push_back(z);
				if(z==(i>>1))	break;
			}
		}
		low[x]=min
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值