【NOI2017】轮回

Description

掌管着世界的暗流的是一个叫做Samjia的人。
他看到所有人的生死,他看见所有人一世又一世的轮回,而他却从未把握过自己的命。
在无法估计的命中,他看见那些轮回,他很好奇,这一切的一切,都是如何开始如何结束,他想,就算是他也会堕入这样的轮回中的吧。
于是他开始数轮回,他看到的是一个有n个点m条边的无向图(边是带标号的),一个轮回是一个由四条边组成的环,环中不能有重复的边,除了起点和终点外(当然,由于是一个环,起点和终点是一样的)不能经过一个同样的点多次。两个轮回被视为不同的当且仅当两个轮回各自的4条边中有一条的编号不同,Samjia想知道,这个system中有多少不同的轮回。
他忙着思考人生,所以数轮回的任务就交给你了。

Solution

大概思路就是枚举其中三个点,然后计算方案数。
这道题目用到了一种很实用的思想,我们枚举一个点 i ,枚举它的下一个点j时,满足点 j 的点度数≥点i的点度数(要避免相等,所以设一个优先级,度数相同的优先级不会相同),再下一个点 k 也同样满足,而这样的时间复杂度大大降低。因为正常的枚举方式要么是枚举点的O(n3),或者是枚举边的 O(m2) ,而这种枚举方式则是 O(nm)

Proof

假设一个点的大于等于这个点度数的点数为 x ,而这样的一幅图的边数最大为x2条,同时满足题设时就有 x22m ,移项就有 x2m
我们把方案数挂在 k 点上,能够成为答案的方案一定包含从i点经过 j 点到达k点后,再从 k 点到另一个j点回到 i <script type="math/tex" id="MathJax-Element-19">i</script>点。

Code

#include<algorithm>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,x) for(i=la[x];i;i=ne[i])
typedef long long ll;
const int N=5e4+5,maxn=1e6+5;
int la[N],ne[maxn*2],da[maxn*2],a[N],t[N],du[N],lv[N],wz[N],d[N];
int n,m,sum,i,j,k,x,y;
ll ans;
bool p[N];
void ins(int x,int y){
    da[++sum]=y,ne[sum]=la[x],la[x]=sum;
}
int main(){
    freopen("palingenesis.in","r",stdin);
    freopen("palingenesis.out","w",stdout);

    scanf("%d%d",&n,&m);
    fo(i,1,m){
        scanf("%d%d",&x,&y);
        du[x]++,du[y]++;
        ins(x,y),ins(y,x);
    }
    fo(i,1,n) t[du[i]]++;
    fo(i,1,n) t[i]+=t[i-1];
    for(i=n;i>0;i--) 
        lv[i]=t[du[i]]--;
    fo(i,1,n){
        sum=0;
        memset(wz,0,sizeof(wz));
        rep(j,i) if(lv[i]<lv[da[j]]){
            rep(k,da[j]) 
            if(lv[i]<lv[da[k]]){
                if(i==da[k]) continue;
                if(!wz[da[k]]){
                    d[++sum]=0;wz[da[k]]=sum;
                }
                d[wz[da[k]]]++;
            }
        }
        fo(j,1,sum) ans+=d[j]*(d[j]-1)/2;
    }
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值