hdu 6411 带劲的and和 题解(思维,好题,并查集)

原题链接:
hdu

题意简述

给定一个无向图,点数1e5,点权1e9,设 f ( u , v ) f(u,v) f(u,v)表示: u , v u,v u,v能否联通(珂以间接)。
求:对于任意 u , v u,v u,v,加和 f ( u , v ) ∗ ( u & v ) ∗ m a x ( u , v ) f(u,v)*(u\&v)*max(u,v) f(u,v)(u&v)max(u,v)

思路

上一篇类似的求和风格。首先,我们要知道啥时候 f ( u , v ) = 1 f(u,v)=1 f(u,v)=1,就是 u , v u,v u,v一个联通块里面。考虑使用并查集维护联通块,然后把每个联通块里的点存到 v e c t o r &lt; i n t &gt; vector&lt;int&gt; vector<int>中。

接着就是求后面了。 m a x ( u , v ) max(u,v) max(u,v)的思路已经知道了,我们对于每个联通块的元素排序,枚举 i i i,去找 i i i和前面的答案。这样就能保证 m a x ( u , v ) = i max(u,v)=i max(u,v)=i了。

接下来就是要求前面所有元素和 i i i a n d and and和。如何求这个 a n d and and和呢?我的老师教导我,遇到这种位运算的东西,就按位求答案。我们设 c n t [ k ] cnt[k] cnt[k]表示:前面的数中,多少个数二进制第 k k k位是 1 1 1

注释(如果不想看,大可跳过):
x x x的第 y y y位是 1 1 1被这么定义: ( ( x &gt; &gt; y ) &amp; 1 ) = = 1 ) ((x&gt;&gt;y)\&amp;1)==1) ((x>>y)&1)==1)

那么,当我们枚举到 i i i的第 j j j位的时候,如果 i i i有第 j j j位,就会有 c n t [ j ] cnt[j] cnt[j]个数和 i i i a n d and and时,在第 j j j位上有答案。此时对答案的贡献就是 ( 1 &lt; &lt; j ) ∗ c n t [ j ] (1&lt;&lt;j)*cnt[j] (1<<j)cnt[j]

然后这样枚举就好了。复杂度是 O ( 32 ∗ n ) O(32*n) O(32n)
代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 100100
    #define mod 1000000007ll
    #define ll long long
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define CLS(x) memset(x,0,sizeof(x))
    class DSU//并查集
    {
        public:
            int Father[N],Cnt[N];
            void Init()
            {
                for(int i=0;i<N;i++)
                {
                    Father[i]=i;
                    Cnt[i]=1;
                }
            }
            int Find(int x)
            {
                return (x==Father[x])?x:(Father[x]=Find(Father[x]));
            }
            void Merge(int x,int y)
            {
                int ax=Find(x),ay=Find(y);
                Father[ax]=ay;
            }
    }D;

    int a[N];
    int n,m;
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Input()
    {
        R1(n),R1(m);
        D.Init();
        for(int i=1;i<=n;++i)
        {
            R1(a[i]);
        }
        for(int i=1;i<=m;++i)
        {
            int u,v;
            R1(u),R1(v);
            D.Merge(u,v);
        }
    }

    vector<int>nodes[N];
    int cnt[32];//cnt[i]:how many (1<<i) appeared previously
    //我习惯在写代码的时候加英文注释,懒得删了
    void Soviet()
    {
        for(int i=1;i<=n;++i)//记得清空
        {
            nodes[i].clear();
        }
        for(int i=1;i<=n;++i)//枚举并查集,并保存好每个vector
        {
            int an=D.Find(i);
            nodes[an].push_back(a[i]);
        }
        ll ans=0;
        for(int i=1;i<=n;++i)
        {
            if (nodes[i].size()==0) continue;

            CLS(cnt);//每次都要初始化!!!
            sort(nodes[i].begin(),nodes[i].end());//排序,方便处理max

            for(int j=0;j<nodes[i].size();++j)
            {
                int val=nodes[i][j];
                for(int k=0;k<32;++k)//枚举每一位,记录cnt值
                {
                    if (val==0) break;
                    if (val&1) ++cnt[k];
                    val>>=1;
                }
            }
            for(int j=nodes[i].size()-1;j>=0;--j)
            {
                int val=nodes[i][j];
                int tmp=val;
                for(int k=0;k<32;++k)
                {
                    if (tmp==0) break;
                    if (tmp&1)
                    {
                        --cnt[k];
                        ll sum=1;
                        sum*=val;sum%=mod;
                        sum*=(1ll<<k);sum%=mod;
                        sum*=cnt[k];sum%=mod;
                        ans+=sum;ans%=mod;
                        //ans*=val*(1<<k)*cnt[k];
                    }
                    tmp>>=1;
                }
            }
        }
        ans=(ans%mod+mod)%mod;
        printf("%lld\n",ans);
    }

    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        int t;R1(t);
        while(t--)
        {
            Input();
            Soviet();
        }
    }
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值