题意:
定义简单环:一个点数和边数相等的回路,并且这条回路上没有出现重复的点或边。
定义项链:
定义 “项链” 是由一些简单环组成的子图,不妨设项链包括 k个简单环 C1C1, C2, ……, Ck (x`),那么项链需要满足:
- 当且仅当 ∣i−j∣≤1 时,简单环 Ci和 Cj 共用顶点;
- 简单环 Ci 和 Ci+1 恰好共用一个顶点;
- 任意两个不同的简单环 Ci和 Cj (i≠j) 没有共用边。
然后给你n个点,m条边,一开始图是空图
操作是按读入顺序把每一条加进去,每加入一条边,你都要计算整个图里面有多少点对(u,v)
满足存在一条项链C1,C2,......,CK(k可以等于1) 使得u∈C1,v∈CK,同时认为(u,v)=(v,u)
假定f(i)为加入i条边之后,满足条件的(u,v)的数量,现在让你求出
解析:
这道题后来想的时候,把题目化简成,其实你只要维护一棵树就可以了
每加入一条边,如果产生环了,那么就要把整个环合成一个点,并计算贡献
但是当时想不出怎么树上在线并环,因为题目给的是一颗森林,会出现后面两棵树接成一棵树的情况,这样根就没办法确定
这样就没有办法找出并环的时候,环上的所有边。
再到后来还想歪了...想到倒着做,先用无向图tarjan缩点,找环,再一条一条边删掉,每删一条边,用
拓扑排序计算删掉的贡献,但是这样后来也找到了反例。。。。
那么官方题解
双连通分量又分点双连通分量和边双连通分量两种。若一个无向图中的去掉任意一个节点(一条边)都不会改变此图的连通性,即不存在割点(桥),则称作点(边)双连通图。一个无向图中的每一个极大点(边)双连通子图称作此无向图的点(边)双连通分量。求双连通分量可用Tarjan算法。
他这里首先就是解决我上面说的根在插入边的时候会变的问题。
就是先把图建成一棵树,这里要以编号为权值,运用Kruskal建生成树,
为什么要用编号小的点?一个环的形成,肯定是有一条最后加入的边,使得其形成一个环。
那么这条边的加入顺序肯定是在这个环上所有边的后面,所以只有当这条边加入的时候,这个环才能
计算贡献。那么我们建树的时候会不会把这条边加入进去呢?不会,因为建树是永远不可能形成环的,
如果不会形成环,并且还把这条边加进入的话,说明这个环里面还有比这条边加入顺序更靠后的边
那么就不满足我们上面假定的条件了。那么如果不按顺序建树的话会出现的后果就是,假如一个环是
应该在假如第6条边的时候,贡献答案的,那么你把第6条边假如到树里面,使得第4条边(环上的边)
没有加入树。那么这个环会在第4条边加入的时候就成环,并贡献答案,这样f(4),f(5)的答案就不对了。
然后就是就是合并树上的环了。这个合并环,其实就是暴力版的倍增。
倍增我们是用2的次幂来优化,这里不能用的原因是这里的树是在线变化的,倍增的数组只能离线处理。
那么当加入一条边,使得树上形成环的后,其实就是找加入的这条边的两个端点的LCA,最朴素版的LCA,其实就是用一个while
while(u!=v){
if(d[u]<d[v]) swap(u,v);
u=fa[u];
}
每一次,让深度更高的点,向上走一步,这样两个点最后到的一定是他们的LCA
那么我们需要在两个点向上走的过程中,经过的点,都加入到一个并查集里面就可以了,
那么当两个点走到一起时,这个环上的点就都加入到一起了。
但是如果你就这么写是会T的,这也是这道题坑的地方...
你在向上合并并查集的时候不能只将ufset[孩子]=父亲,这样这个复杂度就接近于O(n*n)
因为孩子这个并查集里面的点,都还要重新指向父亲。
这里就需要按秩/大小合并并查集....这两个我感觉是差不多,按秩是按树高来合并,其实也就是按并查集大小合并
并查集按大小合并也叫启发式合并,小的指向大的。
那么我们在合并一个点x,以及他的父节点y(注意,父节点可能也是一个环,但是我们现在都把他当作点来处理,在写代码的时候
x真正合并的是findfa(fa[val[x]]),val[]这个我们后面会讲到)
这两个点我们按秩合并的话,假定rank[x]>rank[y],那么y要并入x。正是因为可能存在这种上面的点并入下面的点,所以我们
还需要维护一个val[i],表示i这个并查集(环),深度最低的点,也就是最上面的点。这样我们以后在向上合并的时候,就可以
找到这个并查集的父节点是谁了。
这道题还有一个注意的是,他最后的图可能不是连通的,也就是说,我们Kruskal建出来的可能是一颗森林。
那么我们在dfs确定根节点的时候就要用for(int i=1;i<=n;i++) if(!d[i]) dfs(i)
我这个for没加一直在T......
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
const int N=1e6+100;
const int INF=1<<30;
namespace fastIO {
#define BUF_SIZE 1000000
//fread -> read
bool IOerror = 0;
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if(p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if(pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
inline void read(int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror)
return;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
#undef BUF_SIZE
};
using namespace fastIO;
struct edge{
int fr,to,nxt;
int id;
};
int head[N];
edge f[N*4];
int siz[N];
int fa[N];
int vis[N*2];
int record[N*2][2];
//int ans[N*2];
int ufset[N],m,d[N];
int ran[N];
int val[N];
int fcnt;
void add2(int fr,int to,int id){
f[fcnt].fr=fr;
f[fcnt].to=to;
f[fcnt].id=id;
f[fcnt].nxt=head[fr];
head[fr]=fcnt++;
}
int findfa(int x)
{
if(x==ufset[x]) return x;
int nex=ufset[x];
ufset[x]=findfa(nex);
return ufset[x];
}
void Kruskal()
{
for(int i=1;i<=m;i++)
{
int fx=findfa(record[i][0]);
int fy=findfa(record[i][1]);
if(fx!=fy){
vis[i]=1;
if(ran[fx]>ran[fy]) ufset[fy]=fx,ran[fx]=max(ran[fx],ran[fy]+1);
else ufset[fx]=fy,ran[fy]=max(ran[fy],ran[fx]+1);
add2(record[i][0],record[i][1],i);
add2(record[i][1],record[i][0],i);
}
}
}
void dfs(int x,int fc,int dep)
{
fa[x]=fc;
d[x]=dep;
for(int i=head[x];i!=-1;i=f[i].nxt){
if(f[i].to!=fc){
dfs(f[i].to,x,dep+1);
}
}
}
ll res;
inline int Merge(int u,int v){
if(ran[u]<ran[v]) swap(u,v);
ran[u]=max(ran[u],ran[v]+1);
ufset[v]=u;
if(d[val[u]]>d[val[v]]) val[u]=val[v];
res=res+1ll*siz[u]*siz[v];
siz[u]+=siz[v];
return u;
}
inline void solve(int u,int v)
{
while(u!=v){
if(d[val[u]]<d[val[v]]) swap(v,u);
u=Merge(findfa(fa[val[u]]),u);
u=findfa(u);
v=findfa(v);
}
}
int main(){
int t;
//scanf("%d",&t);
read(t);
int n;
while (t--)
{
fcnt=0;
//scanf("%d%d",&n,&m);
read(n),read(m);
for(int i=1;i<=n;i++)
{
head[i]=-1;
siz[i]=0;
ufset[i]=i;
fa[i]=i;
d[i]=0;
ran[i]=1;
}
for(int i=1;i<=m;i++) vis[i]=0;
for(int i=1;i<=m;i++)
{
int u,v;
//scanf("%d%d",&u,&v);
read(u),read(v);
record[i][0]=u,record[i][1]=v;
}
Kruskal();
for(int i=1;i<=n;i++)
if(!d[i]) dfs(i,0,1);
for(int i=1;i<=n;i++) ufset[i]=i,siz[i]=1,ran[i]=1,val[i]=i;
ll ans=0;
res=0;
for(int i=1;i<=m;i++)
{
if(vis[i]==0) {
int u=findfa(record[i][0]);
int v=findfa(record[i][1]);
if(u!=v)
{
solve(u,v);
}
}
ans=ans^(i*res);
}
printf("%lld\n",ans);
}
}