zoukankan      html  css  js  c++  java
  • 【安徽集训】Entropy

    出题人罗哲正是神爷 Orz

    Description

      这是一道披着交互题外衣的通信题,只支持 C++。
      你需要实现 (2) 个函数。
      交互库先给第一个函数传入一个参数 (n),你加密得到的 (01) 字符串的长度必须是 $n。你需要根据 (n) 做一些相应的预处理,并向交互库返回你能接受的最大 ( ext{long long}) 类型整数 (m)
      先根据你返回的 (m) 给分。若 (m_{you}gt m_{ans}) 则你得 (0) 分,若 (m_{you}=m_{ans}) 则你得 (100) 分,否则你得 (0.5frac{ln{m_{sum}}}{ln{m_{ans}}}) 分。

      这题出到这里可以结束了。但出题人为了验证你的 (m) 是算的还是蒙的,又给了 (Q) 次操作,每次操作中:
        交互库向第一个函数输入一个 ([0,m-1])( ext{long long}) 类型的数,你可以用任意加密手段,将该数加密成一个 (n)(01) 字符串并返回给交互库。
        然后交互库向第二个函数输入你刚才加密得到的密串,这个密串有可能被交互库恶搞,导致该串所有位取反,然后整个串翻转。你需要用适当的解密手段,将其解密为一开始向第一个函数输入的 ( ext{long long}) 类型的数,并返回给交互库。
        每次操作后,交互库会判断第二个函数返回的数 是否和一开始向第一个函数输入的数一样。

      一旦有一次操作加密前的数和解密后的数不一样,你就得不到解密分数,即之前根据 (m) 得的分数打折 (20\%)
      有防作弊机制(即开全局变量记录原数),详见原题面。

      subtask1 (30pts):(nle 18)
      subtask2 (70pts):(nle 60)
      对于所有数据,(0le Qle 50000)
      
      由于交互题描述很长,我懒得一字不差地搬完,下面放上完整题面,但问题描述写得有点恶心,我一开始没看懂



    Solution

      设 (k) 是长度为 (n) 且被恶搞后不变的串的数目。

      先考虑 (n) 是奇数的情况。
      显然翻转之后正中间的一个 bit 一定会取反,此时 (k=0,space m=2^{n-1})。编码方式也很简单,让正中间的 bit 为 (0),解码时若正中间的 bit 为 (1) 则执行恶搞操作。

      然后是(我)想不到的 (n) 是偶数的情况。
      不难算出 (k=2^{frac{n}{2}}),因为一个串被恶搞后与原串相同,当且仅当这个 (01) 串的第 (1) 位与第 (n) 位不同,第 (2) 位与第 (n-1) 位不同,第 (3) 位与第 (n-2) 为不同,以此类推,共 (frac{n}{2}) 组对称位。这些被恶搞后不变的串互不混淆,都可以作为密串
      对于其余所有被恶搞后有变化的串,一定是两两配对,共 (frac{2^n-2^{frac{n}{2}}}{2}) 对。每一对中我们只能使用一个数作为加密后的串,因为一对中的 (2) 个都选的话,万一交互库把你加密后的串恶搞了,你就无法区分这两种密串,也就不知道应该用哪种密串解密出原数。
      那么在密串长度为 (n) 的限制下,共有 (2^{frac{n}{2}} + frac{2^n-2^{frac{n}{2}}}{2} = 2^{n-1} + 2^{frac{n}{2}-1}) 种互不混淆的密串。
      神奇操作:我们可以把 ([0,m-1]) 种所有整数一一映射到这些密串,故 (m=2^{n-1} + 2^{frac{n}{2}-1})

      那我们怎么构造一种映射方式呢?
      我们肯定得从密串中抠出 (1)(2) 位,来标记这个串是否被恶搞,不然肯定没法做。
      而且为了固定这个标记位,即被恶搞后不会翻转到对称位(你的第 (2) 个程序无法判断收到的密串的标记位有没有被翻到对称位,因为你只能通过标记位判断密串有没有翻转),若抠出 (1) 位,那么串长 (n) 必须是奇数,而这里是偶数,舍。故我们要从密串中抠出 (2) 位,且这 (2) 位对称。
      抠哪里呢?
      这里一般就是盲猜结论了,大佬们都觉很简单……当然我可以写一个推法(?)

      把 (m=2^{n-1} + 2^{frac{n}{2}-1}) 的值写成二进制数,发现只有 (2) 位为 (1),故该值也写成 (2^{frac{n}{2}} - sumlimits_{k=1}^{n/2} 2^{n-k-1})
      (2^{n-k-1}) 可以拆成 (2^{k-1} 2^{n-2k})
      观察一下这是什么?设抠掉了密串的第 (k) 位和第 (n-k+1) 位,(k-1) 表示的是密串前 (k-1) 位和后 (k-1) 位,(n-2k) 表示的是密串的第 (k+1) 位到第 (n-k) 位。那么,第 (k) 位和第 (n-k+1) 位就被空出来了!可以把这两位当作标记位!
      设 (x) 为满足密串第 (x) 位不等于第 (n-x+1) 位条件下的最小值,我们把所有密串按 (x) 从小到大分类。显然 (x) 的范围是 ([1,frac{n}{2}+1]),其中 (x=frac{n}{2}+1) 的意义比较特殊,表示每一组对称位上的数都相同。
      由于前 (x-1) 组对称位上的数都相同,故给每一位放一个 (0)(1),方案数为 (2^{x-1});第 (x+1)(frac{n}{2}) 组对称位上的数没有要求,故给每一位放一个 (0)(1),方案数为 (2^{n-2x})
      发现这种设标记位的方式 恰好可以映射 (m) 个数。于是我们可以据此构造映射方式了!

      映射方式很简单,我们把密串看成二进制数,自创一个二进制数比大小的规则,以确定 (m) 个用于映射的二进制数的排名,其排名 (-1) 就是映射到的 ([0,m-1]) 中的原数。
      规则:以 (x) 从小到大为第一关键字,第 (1)(n-x+1) 组成的二进制数从小到大为第二关键字。

      有了规则,设原数为 (a),则加密时求排第 (a+1) 名的密串,解密时求密串的排名 (-1) 即可。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
     
    class Entropy
    {
        public:
            int n;
            ll getM(int _n)
            {
                n = _n;
                if(n&1) return 1ll<<(n-1);
                else return (1ll<<(n-1))|(1ll<<((n>>1)-1));
            }
            string getstr(char x) {string ret; ret=ret+(x==0?"0":"1"); return ret;}
            string encode(ll x)
            {
                char str[65];
                for(int i=0; i<n-1; i++) str[i] = (x&(1ll<<(n-2-i)))?1:0;
                string ret;
                if(n&1)
                {
                    for(int i=0; i<((n-1)>>1); i++) ret = ret+getstr(str[i]);
                    ret = ret+"0";
                    for(int i=((n-1)>>1); i<n-1; i++) ret = ret+getstr(str[i]);
                }
                else
                {
                    for(int i=0; i<=(n>>1); i++)
                    {
                        ll avail = i<(n>>1)?n-i-2:i,y = (1ll<<avail);
                        if(x<y)
                        {
                            for(int j=0; j<i; j++) {avail--; ret = ret+(x&(1ll<<avail)?"1":"0");}
                            if(i<(n>>1)) ret = ret+"0";
                            for(int j=i+1; avail; j++) {avail--; ret = ret+(x&(1ll<<avail)?"1":"0");}
                            if(i<(n>>1)) ret = ret+"0";
                            for(int j=i-1; j>=0; j--) {ret = ret+getstr((ret[j]-'0')^1);}
                            break;
                        }
                        x -= y;
                    }
                }
                return ret;
            }
            ll decode(string str)
            {
                char str0[65]; for(int i=0; i<n-1; i++) str0[i] = str[i]-'0';
                ll ret = 0ll;
                if(n&1)
                {
                    if(str[n>>1]=='1')
                    {
                        for(int i=n-1; i>=0; i--)
                        {
                            if(i==(n>>1)) continue;
                            ret = (ret<<1)|(str[i]=='0'?1ll:0);
                        }
                    }
                    else
                    {
                        for(int i=0; i<n; i++)
                        {
                            if(i==(n>>1)) continue;
                            ret = (ret<<1)|(str[i]=='1'?1ll:0);
                        }
                    }
                }
                else
                {
                    for(int i=0; i<=(n>>1); i++)
                    {
                        if(i==(n>>1) || str[i]==str[n-1-i])
                        {
                            ll tmp = 0ll;
                            if(str[i]=='0')
                            {
                                for(int j=0; j<i; j++) {tmp = (tmp<<1)|(str[j]=='1'?1:0);}
                                for(int j=i+1; j<n-i-1; j++) {tmp = (tmp<<1)|(str[j]=='1'?1:0);}
                            }
                            else
                            {
                                for(int j=n-1; j>n-i-1; j--) {tmp = (tmp<<1)|(str[j]=='0'?1:0);}
                                for(int j=n-i-2; j>i; j--) {tmp = (tmp<<1)|(str[j]=='0'?1:0);}
                            }
                            ret += tmp;
                            break;
                        }
                        ret += (1ll<<n-i-2);
                    }
                }
                return ret;
            }
    };
    #include "entropy.h"
    
  • 相关阅读:
    用 Python 带你看各国 GDP 变迁
    Fluent Interface(流式接口)
    probing privatePath如何作用于ASP.NET MVC View
    Word插入htm文件导致文本域动态增加的一个问题
    Visual Studio 2013附加进程调试IE加载的ActiveX Control无效解决方法
    Ubuntu下Chrome运行Silverlight程序
    Windows Phone Bing lock screen doesn't change解决方法
    SPClaimsUtility.AuthenticateFormsUser的证书验证问题
    Web Service Client使用Microsoft WSE 2.0
    Visual Studio 2013安装Update 3启动crash的解决方法
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/11598612.html
Copyright © 2011-2022 走看看