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"
    
  • 相关阅读:
    多维梯度下降
    梯度下降
    三种评价函数
    Gluon sgd
    Gluon.vision的几类数据集
    Gluon Data API
    Gluon 实现 dropout 丢弃法
    AlexNet 分类 FashionMNIST
    LeNet 分类 FashionMNIST
    LeNet
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/11598612.html
Copyright © 2011-2022 走看看