1 solutions

  • 0
    @ 2025-8-15 15:03:07

    Bitset

    可达性用邻接矩阵维护,显然有一个 O(nmw)O(\frac{nm}{w}) 的做法,但是需要注意常数,本人刚开始无脑每个点算 4 次狠狠 TLE,通过简单整理可以每个点仅算一次。此外,由于 bitestcount() 复杂度为 O(n/w)O(n/w),应该开个数组记录度数代替,不这样做的在洛谷上能通过但本 oj 会 TLE。

    以下代码可以在此 oj 通过:

    using namespace std;
    typedef long long ll;
    const int maxn=3e4+2,maxm=3e5+10;
    int n,m,x[maxm],y[maxm],deg[maxn];
    bitset<maxn> b[maxn];
    ll ans[7][maxn];
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n>>m;
    	for(int i=1;i<=m;i++) {
            cin>>x[i]>>y[i];
    		b[x[i]][y[i]]=b[y[i]][x[i]]=1;
            deg[x[i]]++;deg[y[i]]++;
    	}
    	for(int i=1;i<=n;i++) ans[2][i]=m;
    	for(int i=1;i<=m;++i) {
    		int bxby=(b[x[i]]&b[y[i]]).count();
    		int bxcy=deg[x[i]]-bxby;
    		int bycx=deg[y[i]]-bxby;
    		int cxcy=n-bxby-bxcy-bycx;//(c[x[i]]&c[y[i]]).count();
    		ans[3][x[i]]+=cxcy;//减去(u,v,u)(u,v,v)
    		ans[3][y[i]]+=cxcy;//减去(u,v,u)(u,v,v)
    		ans[4][x[i]]+=bxcy-1;//减去(u,v,v)
    		ans[4][y[i]]+=bycx-1;//减去(u,v,v)
    		ans[5][x[i]]+=bycx-1;//减去(u,v,v)
    		ans[5][y[i]]+=bxcy-1;//减去(u,v,v)
    		ans[6][x[i]]+=bxby;
    		ans[6][y[i]]+=bxby;
    		ans[2][x[i]]--;
    		ans[2][y[i]]--;
    	}
    	ll tot=1ll*(n-1)*(n-2)/2;
    	for(int i=1;i<=n;i++) {
    		ans[6][i]>>=1;ans[4][i]>>=1;
    		ans[2][i]-=ans[5][i]+ans[6][i];
    		ans[1][i]=tot-ans[2][i]-ans[3][i]-ans[4][i]-ans[5][i]-ans[6][i];
    		for(int j=1;j<=6;j++) {
    			ll g=__gcd(ans[j][i],tot);
                cout<<ans[j][i]/g<<"/"<<tot/g<<" ";
    		}
            cout<<"\n";
    	}
    	return 0;
    }
    

    无向图三元环计数+解方程

    于我而言,这就是一道解方程题(大雾)

    首先考虑概率如何统计,注意到其他人的分组与自己其实毫无关系,状态空间仅看做从除自己外 n1n-1 个人中选择 22 个人分组即可。

    方便起见,在此规定(与题目规定顺序相同):

    degideg_i: 第 ii 个同学的好友数;

    x1x_1: 3 个学生两两均不为好友;

    x2x_2: 3 个学生中,除自己外的 2 个学生互为好友,不存在其他好友关系;

    x3x_3: 3 个学生中,自己与另外某个学生互为好友,不存在其他好友关系;

    x4x_4: 3 个学生中,恰好有 2 对好友关系,且有 2 个好友的那个人是自己(即:自己与另外 2 个学生分别互为好友,但他们两个不为好友);

    x5x_5: 3 个学生中,恰好有 2 对好友关系,但有 2 个好友的那个人不是自己(即:存在某个学生 A 与自己和另外一个学生 B(分别)互为好友,但自己与 B 不为好友);

    x6x_6: 3 个学生中两两互为好友。

    通过观察,我们可以列出如下方程:

    情况 2,5,6 枚举了所有不与 ii 相连的边:

    x2+x5+x6=mdegi(1)x_2+x_5+x_6=m-deg_i\quad(1)

    枚举所有与 ii 相连的边与任意一个其他点(注意到情况 4,6 具有对称性):

    x3+2x4+x5+2x6=degi×(ni)(2)x_3+2x_4+x_5+2x_6=deg_i\times(n-i)\quad(2)

    这是总方案数:

    x1+x2+x3+x4+x5+x6=(n1)(n2)/2(3)x_1+x_2+x_3+x_4+x_5+x_6=(n-1)(n-2)/2\quad(3)

    情况 4,6 枚举了所有与 ii 相连的边:

    x4+x6=degi(degi1)/2(4)x_4+x_6=deg_i(deg_i-1)/2\quad(4)

    情况 5,6 描述了自己认识的人,认识另一人的情形:

    $$x_5+x_6=\Sigma_j[e_{jx}==i]deg_{e_{jy}}+\Sigma_j[e_{jy}==i]deg_{e_{jx}}\quad (5) $$

    55 个方程 66 个未知数怎么办呢?我们可以敏锐地发现情况 6 就是无向图三元环计数,x6x_6 可以 O(mm)O(m\sqrt m) 求出来,那么,问题解决。

    代码如下:

    using namespace std;
    typedef long long ll;
    const int maxn=3e5+5;
    inline int read() {
        char ch=getchar();bool f=0;int x=0;
        for(;!isdigit(ch);ch=getchar())if(ch=='-')f=1;
        for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
        if(f==1)x=-x;return x;
    }
    int n,m,u[maxn],v[maxn],deg[maxn],vis[maxn];
    vector<int> e[maxn];
    ll ans[7][maxn];
    int main() {
        n=read(),m=read();
        for(int i=1;i<=m;i++)
            u[i]=read(),v[i]=read(),deg[u[i]]++,deg[v[i]]++;
        for(int i=1;i<=m;i++) {
            if(deg[u[i]]>deg[v[i]]||(deg[u[i]]==deg[v[i]]&&u[i]>v[i])) swap(u[i],v[i]);
            e[u[i]].push_back(v[i]);
            ans[5][u[i]]+=deg[v[i]]-1;
            ans[5][v[i]]+=deg[u[i]]-1;
        }
        for(int i=1;i<=n;i++) {
            for(int j : e[i]) vis[j]=1;
            for(int j : e[i]) for(int k : e[j])
                if(vis[k]) ans[6][i]++,ans[6][j]++,ans[6][k]++;
            for(int j : e[i]) vis[j]=0;
        }
        ll tot=(n-1)*(n-2)/2;
        for(int i=1;i<=n;i++) {
            ans[4][i]=1ll*deg[i]*(deg[i]-1)/2-ans[6][i];
            ans[5][i]-=ans[6][i]*2;
            ans[2][i]=m-deg[i]-ans[5][i]-ans[6][i];
            ans[3][i]=1ll*deg[i]*(n-2)-2*ans[4][i]-ans[5][i]-2*ans[6][i];
            ans[1][i]=tot-ans[2][i]-ans[3][i]-ans[4][i]-ans[5][i]-ans[6][i];
            for(int j=1;j<=6;j++) {
                ll g=__gcd(tot,ans[j][i]);
                printf("%lld/%lld ",ans[j][i]/g,tot/g);
            }
            puts("");
        }
        return 0;
    }
    

    Information

    ID
    39
    Time
    1000ms
    Memory
    512MiB
    Difficulty
    7
    Tags
    # Submissions
    35
    Accepted
    2
    Uploaded By