Codeforces 724G Xor-matic Number of the Graph

14 篇文章 0 订阅
1 篇文章 0 订阅

Problem

Codeforces

Solution

这道题目思路会比较像wc2011的xor,可能会更难一点,是一道线性基的好题

你得知道维护对角矩阵的线性基保证对于所有线性基,二进制任意一位最多只有一个为1。但由于构造对角矩阵的线性基时间复杂度为 O(nlog2n) O ( n l o g 2 n ) ,所以我们一般(包括下文中的代码)写的是维护上三角矩阵的线性基。如果这个线性基第i位至少有一个1,那么显然前者也会有。

首先可以用一个O(n)的dfs处理出所有的环的异或和,我们只需记录一个异或和即可。更形象地说,dfs就相当于搜成一棵树,开始的节点就是根节点,一旦碰到已经在树上的点,由于异或的性质,dis[x] xor w xor dis[y]就相当于这个环的异或和,当然了可能没计入的大环是可以由小环组合而来的。由于图不保证联通,那么分联通块计算。

我们先枚举这两个点,容易知道dis[x] xor dis[y]就是两点路径的异或和
按位来考虑贡献,设当前处理的是第i位,要有贡献,我们需要分情况讨论。
不妨记线性基有tot个,dis[x]第i位为0的个数有cnt[0]个,对cnt[1]也是同样的定义

首先如果两点路径的异或和第i位为0的话,那么第i位做贡献,必须异或一些环使得最后第i位为1。选中这一位的线性基,其他的任意组合。那么贡献为
(C2cnt[0]+C2cnt[1])2i2tot1 ( C c n t [ 0 ] 2 + C c n t [ 1 ] 2 ) ∗ 2 i ∗ 2 t o t − 1

另一种情况,我们在线性基中找有没有第i位为1的,如果没有,那么就可以tot个任意组合,如果有,那么去掉它再任意组合
cnt[0]cnt[1]2f?tot1:tot c n t [ 0 ] ∗ c n t [ 1 ] ∗ 2 f ? t o t − 1 : t o t

Code

关于调试的一个小trick,编译的时候加-ftrapv命令,如果re就说明爆int/long long了

#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010,mod=1000000007;
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
struct data{int v,nxt;ll w;}edge[maxn<<2];
int n,m,p,sz,fac[70],q[maxn],head[maxn];
ll ans,dis[maxn],l[70];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void insert(int u,int v,ll w)
{
    edge[++p]=(data){v,head[u],w};head[u]=p;
    edge[++p]=(data){u,head[v],w};head[v]=p;
}
void input()
{
    int u,v;ll w;
    read(n);read(m);
    for(int i=1;i<=m;i++)
    {
        read(u);read(v);read(w);
        insert(u,v,w);
    }
}
void add(ll x)
{
    for(int i=63;~i;i--)
      if(x&(1ll<<i))
      {
        if(!l[i]){l[i]=x;return ;}
        x^=l[i];
      }
}
void dfs(int x,int pre)
{
    q[++sz]=x;
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v!=pre)
      {
        if(~dis[edge[i].v]) add(dis[x]^edge[i].w^dis[edge[i].v]);
        else dis[edge[i].v]=dis[x]^edge[i].w,dfs(edge[i].v,x);
      }
}
void calc()
{
    int f,tot=0,cnt[2];
    ll now;
    for(int i=0;i<=63;i++) if(l[i]) tot++;
    for(int i=0;i<=63;i++)
    {
        cnt[0]=cnt[1]=f=0;
        for(int j=1;j<=sz;j++) cnt[(dis[q[j]]>>i)&1]++;
        for(int j=0;j<=63&&!f;j++) if(l[j]&(1ll<<i)) f=1;
        now=((ll)cnt[0]*(cnt[0]-1)/2+(ll)cnt[1]*(cnt[1]-1)/2)%mod;
        if(f)
        {
            if(tot) now=now*fac[tot-1]%mod;
            now=now*fac[i]%mod;ans=pls(ans,now);
        }
        now=(ll)cnt[0]*cnt[1];
        if(f) {if(tot) now=now*fac[tot-1]%mod;}
        else now=now*fac[tot]%mod;
        now=now*fac[i]%mod;ans=pls(ans,now);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();fac[0]=1;
    memset(dis,0xff,sizeof(dis));
    for(int i=1;i<70;i++) fac[i]=pls(fac[i-1],fac[i-1]);
    for(int i=1;i<=n;i++)
      if(dis[i]==-1)
      {
        memset(l,0,sizeof(l));
        sz=dis[i]=0;dfs(i,0);calc();
      }
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值