今天考试的三道题的难度都只达到NOIP T2或者超过一点,可我才骗到了10分,实属能力不够,愿下次改进;
这次考试,我不足的知识点有:树上差分,求双连通分量,AC自动机,矩阵,差分约束系统,拓扑排序,线段树优化建图;
T1:困难的图论
给定由n 个点m 条边组成的无向连通图,保证没有重边和自环。
你需要找出所有边,满足这些边恰好存在于一个简单环中。一个环被称为简单环,当且仅当它包含的所有点都只在这个环中被经过了一次。注意到这些边可能有很多条,你只需要输出他们编号的异或和即可。
法一:因为是无向图,所以非树边都是返祖边,则可得:若一条边在一个简单环中
•.返祖边,且它覆盖的树边不和任何其他返祖边覆盖的树边有公共边。
•树边且被一条返祖边覆盖,覆盖它的返祖边覆盖的树边不和其他返祖边覆盖的树边有公共边。
可以利用树上差分的思想计算被多少返祖边覆盖,计算是否有重合相当于链上查询,也可以利用差分做到;
法二:简单环也是环,则考虑求出所有点双连通分量,首先这个点双连通分量里的边要合法的话,那么内部的边数一定大等于点数(因为有环)。其次这个点双连通分量里如果边数大于点数,那么所有边可以由两个不同的环覆盖,所以在这个点双连通分量内只能点数等于边数;
我们只需求出所有的点双连通分量,然后把那些点数等于边数的点双连通分量内部的边标记为合法即可,利用Tarjan 算法可以做到O( n+m)
教练的std用的stack和int,然而在OJ上提交时会runtime error,TMD必须手写栈,而且开long long,数组大小还要控制死,不然就是MIT,真的是一道毒瘤题。
法一代码请见:https://blog.youkuaiyun.com/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