zoukankan      html  css  js  c++  java
  • COCI. Dojave


    dojave(4s256M)

    给定一个正整数M,02M-12M个数的排列,计算机先选择一段连续的子串,你必须选两个不同的元素出来,交换它们的位置,使得计算机选出来的子串在交换后的元素的异或值为2M-1,如果可以,你将获胜。现在请你统计有多少种不同的选法,你可以获胜。

    输入:

    第一行一个正整数M​(1​ ​≤​ ​​M​ ​≤​ ​20)

    接下来是一行,是02M-1 的排列,用空格隔开。

    输出:

    一行一个数,表示可以获胜的方法总数。

    50%的数据​1​ ​≤​ ​​M​ ​≤​ ​14

    SAMPLE​​ ​​TESTS

    input

    2

    0​ ​1​ ​2​ ​3

    Output

    9


    题目大意:

    给定一段序列2^M个数的排列,有多少种方案可以满足最多交换一次使区间异或和为2^M-1

    分析:

    记前缀异或和为S,ed=1<<M
    那么区间异或和为t=S[R]^S[L-1]
    我们记t^g=ed-1
    g=a[A]^a[B](A,B分别为区间内和外的两个数)
    因为给的是个全排列
    所以对于给定的g
    任何一个数C都有D与之异或为g(唯一匹配)

    区间个数为奇数:
    所以如果所选区间个数为奇数
    那么肯定会一个数C落单 便可以和区间外的D进行交换
    那么就肯定可以获胜

    区间个数为偶数:
    如果所选区间个数为偶数
    有数没配对:
    如果其中有数没有匹配 那么它也可以和区间外的D交换
    最后肯定会获胜

    所以数配对:
    会产生len/2个g

    如果g的个数为奇数
    那么t=g g^g=ed-1
    这是不可能的 因为两相同的数异或为0

    所以只要g个数为偶数的情况不获胜
    那么t=0 g=ed-1
    所以区间长度要为4的倍数


    求解:

    确定了g
    我们就两两配对了
    我们为了确保知晓两两配对
    哈希一下
    记录个前缀和 确保出现A 只要出现B才会异或为0

    我们再开4个map
    1 2 3 4 5 6 7
    因为6只有可能从2来
    记录一下当前异或和 如果之前有相同的异或值 说明这之间异或和为0
    这样我们就确保了长度为4的倍数 并且 两两配对

    用总的方案数减去这些不合法的方案数得到答案


    附上代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=(1<<20)+12;
    int M,ed;
    int id[N],a[N],t[N][2];
    
    int rand() {static int seed=703;return seed=1LL*seed*48271LL%0x7fffffff;}
    map<pair<int,int>,int> st[4];
    int main()
    {
        freopen("dojave.in","r",stdin);
        freopen("dojave.out","w",stdout);
        scanf("%d",&M);ed=1<<M;
        long long ans=1LL*(1+ed)*ed/2;
        for(int i=1;i<=ed;i++) scanf("%d",&a[i]),id[a[i]]=i;
        for(int i=0;i<ed/2;i++) 
        {
            t[id[i]][0]=rand();t[id[i]][1]=rand();
            t[id[(ed-1)^i]][0]=t[id[i]][0];
            t[id[(ed-1)^i]][1]=t[id[i]][1];
        }
        for(int i=1;i<=ed;i++) t[i][0]^=t[i-1][0],t[i][1]^=t[i-1][1];
        for(int i=0;i<=ed;i++) 
        {
            ans-=st[i%4][make_pair(t[i][0],t[i][1])];
            st[i%4][make_pair(t[i][0],t[i][1])]++; 
        }
        if(M==1) ans=2;
        printf("%lld
    ",ans);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

  • 相关阅读:
    剑指offer之第一个只出现一次的字符
    剑指offer之求1+2+...n
    剑指offer求两个整数之和(要求在函数体内不得使用+、-、*、/四则运算符号)
    剑指offer之从上往下打印二叉树
    剑指offer之栈的压入、弹出序列(利用辅助栈)
    58笔试-忘记题目
    联通软研院2020年球季校招笔试第三题 20190916
    简单的计算小技巧
    前端访问不到本地图片,IDEA设置Tomcat虚拟路径
    求最大子列和问题
  • 原文地址:https://www.cnblogs.com/Heey/p/9016669.html
Copyright © 2011-2022 走看看