[NOIP模拟][BZOJ4264]小c找朋友

本文详细解析了BZOJ4264题目《小c找朋友》的解题思路。通过随机数策略,分析了满足特定条件的点对(x,y)数量。对于无向图,当点z不等于x或y时,(x,z)和(y,z)要么同时存在要么同时不存在。文章提供了样例输入输出,并给出了两种情况的解决方案:1) xy间无边,计算相同集合个数的组合数;2) xy间有边,通过边连接的点集合判断满足条件的点对。最后附上了相关代码实现。" 49889527,5220311,深入理解Android String资源,"['Android开发', '资源管理', '字符串处理', 'XML布局']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:
题目链接:bzoj 4264 小c找朋友 PS:这是一道权限题。
题目大意:
给定一张无向图,求满足以下条件的点对(x,y)数目:对任意点 z (z!=x,y),边(x,z)和(y,z)同时存在或同时不存在。
输入格式:
一行两个整数 n,m,分别表示这幅图点数和边的数量。
接下来m行,每行两个整数,表示这两个点之间存在一条无向边。保证没有重边和自环。
输出格式:
一行一个整数,表示答案。
样例输入:

3 3
1 2
2 3
1 3

样例输出:
3

数据范围:
对于前30%的数据, n,m500
对于100%的数据, n,m106
题目分析:
一个点对满足上面的要求,则这两个点所连向的点的集合是一样的(除开它们之间互相连)。于是给每个点赋值(随机赋,但有技巧,见代码),分两种情况讨论:
1、xy之间没有边,直接对每个点所连向的点的集合求和(或者异或和),如果两个点的和相同,就说明它们所连向的点的集合相同。于是它们是满足要求的,记和为同一个的个数为n,ans=n*(n-1)/2.
2、xy之间有边。枚举每条边,如果此边所连的两个点的和分别减去对面的点的权值后相同,说明它们满足要求,ans++。
附代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;

const int N=1e6+100;
const int mod=1e3; 
int n,m,tot,nxt[N*2],first[N],to[N*2],a[N],from1,from2,dian1,dian2;
long long w[N],sum[N],b[N],ans,num[N];

int readint()
{
    char ch;int i=0,f=1;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') {ch=getchar();f=-1;}
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
    return i*f;
}

void create(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
}

void discinit()//离散化
{
    sort(b+1,b+n+1);
    m=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(b+1,b+m+1,sum[i])-b;
}

int main()
{
    //freopen("graph.in","r",stdin);
    //freopen("graph.out","w",stdout);

    int x,y;
    n=readint();m=readint();
    tot=1;
    for(int i=1;i<=m;i++)
    {
        x=readint();y=readint();
        create(x,y);
        create(y,x);
    }
    srand(time(0));
    for(int i=1;i<=n;i++)
    {
        x=(rand()%mod)*(rand()%mod)*(rand()%mod)*(rand()%mod)+1;//连续乘4次,使得后面点集不同但和相同的情况的概率非常非常小
        w[i]=x;
    }
    for(int i=1;i<=n;i++)
    {
        for(int e=first[i];e;e=nxt[e])
        {
            sum[i]+=w[to[e]];
        }
        b[i]=sum[i];
    }
    discinit();//离散化,来统计和分别相同的有多少个,也可以用hash求
    for(int i=1;i<=n;i++)
    {
        num[a[i]]++;
    }
    for(int i=1;i<=n;i++)
    if(num[i]>1)
    {
        ans+=num[i]*(num[i]-1)/2;
    }
    for(int i=1;i<=m;i++)
    {
        from1=i*2;from2=i*2+1;
        dian1=to[from1];dian2=to[from2];
        if(sum[dian1]-w[dian2]==sum[dian2]-w[dian1])
            ans++;
    }
    printf("%I64d",ans);

    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值