cd graph

图论专场

Fairy(CF19E)

题目大意

给出一张无向图,求删除一条边后此图变成二分图的所有删边种类 (n104)

思路

虽然看起来 O(n2) 能过,但其实是过不了的,我们知道,判断二分图的一种方式是判断有无奇环,所以问题变成了删去一条边后图中有无奇环,如果有环重合,可以用类似异或的逻辑将中间重复的取消,在看成一个环,明显发现只有偶环+奇环=奇环,然后就做完了。

#include<bits/stdc++.h>
#define ll long long
#define mm 10010
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
struct node{
int nxt,v,id;
}e[mm<<2];
int head[mm<<2],tot=1,dfn[mm];
int n,m,ans[mm],p[mm],f[mm],Ans;
int c1[mm],c2[mm],cnt;
void ADD(int x,int y,int id)
{
e[++tot]=(node){head[x],y,id};
head[x]=tot;
}
void dfs(int x,int fa,int pre)
{
dfn[x]=dfn[fa]+1;
p[x]=e[pre].id;
for(int i=head[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v)
{
if((i^pre)==1)
continue;
if(!dfn[v])
{
dfs(v,x,i);
f[e[i].id]=1;
c1[e[pre].id]+=c1[e[i].id];
c2[e[pre].id]+=c2[e[i].id];
}
else
{
if(x==v && i&1)
++c1[e[i].id],++cnt;
else
{
if(dfn[x]>dfn[v])
if((dfn[x]-dfn[v])&1)
{
++c2[e[i].id];
++c2[e[pre].id];
--c2[p[v]];
}
else
{
++c1[e[i].id];
++c1[e[pre].id];
--c1[p[v]];
++cnt;
}
}
}
}
}
void print()
{
printf("%d\n",Ans);
for(int i=1;i<=Ans;i++)
printf("%d ",ans[i]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
ADD(x,y,i);
ADD(y,x,i);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
dfs(i,0,0);
if(!cnt)
{
for(int i=1;i<=m;i++)
ans[++Ans]=i;
print();
return 0;
}
for(int i=1;i<=m;i++)
if(f[i])
{
if(c1[i]==cnt && !c2[i])
ans[++Ans]=i;
}
else
{
if(cnt==1 && c1[i]==1)
ans[++Ans]=i;
}
print();
return 0;
}

Connecting Cities(AT_keyence2019_e)

题目大意

给定 N 个点的完全图图,每个点有权值 Ai。对于任何点 i 和点 j,这两点之间的边权为 wi,j=Ai+Aj+D|ij|,其中 D 为给定的常数,求最小生成树的大小。(N2×105,Ai,D109)

思路

还是用 primKruskal,但这题不可以全部连边,可以发现有些边是不必要的,先考虑把式子简化,我们如果确定这两个 ij,可以变成 Ai+D×i+AjD×j

既然 ij 独立出来,那么只需要选出,右半部分 minAi+D×i 所对应的 i,左半部分 minAj+D×j 所对应的 j,将 j 与右半部分所有点连边,i 同理,这样边就只有 O(nlogn),最后跑 Kruskal

#include<bits/stdc++.h>
#define ll long long
#define mm 10010
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
struct node{
int nxt,v,id;
}e[mm<<2];
int head[mm<<2],tot=1,dfn[mm];
int n,m,ans[mm],p[mm],f[mm],Ans;
int c1[mm],c2[mm],cnt;
void ADD(int x,int y,int id)
{
e[++tot]=(node){head[x],y,id};
head[x]=tot;
}
void dfs(int x,int fa,int pre)
{
dfn[x]=dfn[fa]+1;
p[x]=e[pre].id;
for(int i=head[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v)
{
if((i^pre)==1)
continue;
if(!dfn[v])
{
dfs(v,x,i);
f[e[i].id]=1;
c1[e[pre].id]+=c1[e[i].id];
c2[e[pre].id]+=c2[e[i].id];
}
else
{
if(x==v && i&1)
++c1[e[i].id],++cnt;
else
{
if(dfn[x]>dfn[v])
if((dfn[x]-dfn[v])&1)
{
++c2[e[i].id];
++c2[e[pre].id];
--c2[p[v]];
}
else
{
++c1[e[i].id];
++c1[e[pre].id];
--c1[p[v]];
++cnt;
}
}
}
}
}
void print()
{
printf("%d\n",Ans);
for(int i=1;i<=Ans;i++)
printf("%d ",ans[i]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
ADD(x,y,i);
ADD(y,x,i);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
dfs(i,0,0);
if(!cnt)
{
for(int i=1;i<=m;i++)
ans[++Ans]=i;
print();
return 0;
}
for(int i=1;i<=m;i++)
if(f[i])
{
if(c1[i]==cnt && !c2[i])
ans[++Ans]=i;
}
else
{
if(cnt==1 && c1[i]==1)
ans[++Ans]=i;
}
print();
return 0;
}

Tournament(CF878C)

题目大意

n 个人,每个人都有 k 种能力值 ai,1ai,2ai,k

定义淘汰赛的规则如下:在没被淘汰的人中选出两个 ij,并选择 d[1,k]。如果 ai,d>aj,d,则 j 淘汰;否则 i 淘汰(保证所有的 ai,d 互不相同)。直到只剩下一个人没有被淘汰,则这个人是最终的胜者。

对于每个 i,需要求出如果初始参加比赛的人是 1,2,...,i,这 i 个人里有多少人可能成为最终的胜者。

n5×104,k10

思路

我们考虑两个选手之间的关系,如果一个选手能在某一项运动中战胜对手,那么就从他自身向对手连一条有向边。这样显然会出现很多环,于是可以缩点,将整张图缩成一条链。那么显然入度为零的环中包含的点数即为最后可能成为冠军的人数。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
int n,k;
struct node{
int siz;
int maxn[15],minn[15];
node(){
memset(maxn,0,sizeof(maxn));
memset(minn,0x3f,sizeof(minn));
}
friend bool operator <(node a,node b){
for(int i=1;i<=k;i++)
if(a.maxn[i]>b.minn[i])
return false;
return true;
}
};
set<node> s;
set<node>::iterator it;
int main()
{
n=read(),k=read();
node t;
for(int i=1;i<=n;i++)
{
t.siz=1;
for(int j=1;j<=k;j++)
t.maxn[j]=t.minn[j]=read();
while((it=s.find(t))!=s.end())
{
t.siz+=it->siz;
for(int j=1;j<=k;j++)
{
t.minn[j]=min(t.minn[j],it->minn[j]);
t.maxn[j]=max(t.maxn[j],it->maxn[j]);
}
s.erase(it);
}
s.insert(t);
printf("%d\n",(--s.end())->siz);
}
return 0;
}
posted @   noipwen  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示