• IT(然而其实是。。hdu5244?)


    Time Limit: 3000 ms Memory Limit: 256 MB

    Description

      IT = Inverse Transform

      两个长度为 (2^n) 的序列 (a,b). ( 下标从 0 到 (2n−1) )

      满足 (0≤a_i≤10^9) , 且 (b)(a) 变换而来, 变换如下:

    [b_i=sumlimits_{0≤j<2^n}f((i∨j)⊕i) a_j ]

      其中 (∨) 表示按位或, (⊕) 表示按位异或. (的二进制中的个数为偶数f(x)=[x的二进制中1的个数为偶数])

      (其中 [expression] 当 expression为真时为 1, 否则为 0)

      现在, (a) 序列被玩丢了...

      请你通过 (b) 序列还原出 (a) 序列

    Input

      第一行一个数 (T) 表示测试数据组数
      接下来对于每组数据 :
      第一行一个整数 (n)
      第二行 (2^n) 个整数, 表示序列 (b)

    Output

      输出包括 (T)
      每行 (n) 个整数, 表示序列 (a)

    Sample Input

    4
    0
    1
    1
    1 3
    2
    5 3 4 10
    3
    101 91 92 104 93 105 108 190
    

    Sample Output

    1
    1 2
    1 2 3 4
    31 24 26 23 27 23 24 12
    

    HINT

      对于10% 的数据 (n≤5)
      对于另外10%​ 的数据(n≤10)
      对于100%​ 的数据 : (T≤5,1≤n≤20)
      数据保证 (b) 序列 能 唯一地对应一个 (a) 序列
      且保证对应的 (a) 序列满足 (0≤a_i≤10^9)

      ( 注意 : 但 不保证 读入的 (b) 序列满足 (0≤b_i≤10^9) )


    Solution

    • 10%

      这个直接大力高斯消元就好了,注意是。。不保证(b)是int所以要用long long。。

    (另外10%不会qwq)

    • 100%

      首先观察一下那个诡异的变换((i∨j)⊕i)(记作(change(x,y))好了)我们考虑一下把不同的(0)(1)组合带进去会得到什么:

    [egin{aligned} change(0,1)=1 change(1,1)=0\ change(0,0)=0 change(1,0)=0\ end{aligned} ]

      会发现带进去的除了((0,1))以外其他全部都是(0)

      然后我们再看一下那个诡异的(f)函数,会发现我们需要考虑的只是(change)后的(1)的个数的奇偶性

      然后因为这个东西每位的运算是独立的,再加上是二进制,可以考虑分治来解决这个问题

      (b)(a)比较烦,那么就先看(a)(b)

      考虑每次将这个数列分成两半,对于([l,mid])中的(b_i),我们只求(b_i=sumlimits_{j=l}^{mid}f(change(i,j))a[j])
      同理([mid+1,r])中的(b_i=sumlimits_{j=mid+1}^{r}f(change(i,j))a[j])
      换句话来说,就是对于分成的每一个区间,我们只求这个区间内对答案的贡献,按照这样的方式,分到最后区间长度为(1)时,我们可以得到(b_i=a_i),然后再将这些分开的区间一层一层地合并,同时统计跨区间的贡献,最后得到完整的(b)数组(有点像。。线段树的pushup一样?)

      那么现在考虑怎么统计跨区间的贡献

      我们考虑将([l,mid])([mid+1,r])合并成([l,r])的情况,考虑这样划分出来的区间的性质,必定是([2^n,2^m-1])这样的形式,记([l,mid])的区间长度为(2^k)([l,mid])([mid+1,r])还满足这两个区间对应的下标写成二进制后,后(k)位是一样的,只有第(k+1)位不同,举([0,1])([2,3])为例子,两个区间对应的下标写成二进制后分别是(00,01)(10,11),对应起来只有第(2)位是不同的,而且左边的一组这位全是(0),右边的一组这位全是(1)

      清楚了这样的性质之后,再考虑合并区间就方便多了

      1

      为了方便我们将([l,mid])记作(bx)(下标从(l)(mid) ),([mid+1,r])记作(by)(下标从(mid+1)(r) ),这两个区间合并后在大区间记作(b)(下标从(l)(r) ),(b)(b'x)(b'y)两个部分组成,其中(b'x)的下标从(l)(mid)(b'y)的下标从(mid+1)(r)

      首先考虑(b'y[i])是怎么得到的,首先肯定是(by[i])加上(sumlimits_{j=l}^{mid}f(change(i,j))a[j])得到的,而由于(bx)(by)的下标满足上面的性质,所以我们只用考虑不同的那一位对(1)的个数的奇偶性的影响

      而因为我们的(change)只有带入((0,1))后得到的是(1),也就是说只有两个数不同的那一位分别为(0)(1)的时候才会对奇偶性有影响,所以我们可以发现,当(i in [mid+1,r])时,(sumlimits_{j=l}^{mid}f(change(i,j))a[j]=bx[i-mid])(因为这样不同的两位带进去是((1,0)),得出来是(0),不会对奇偶性造成影响,而除去不同的那一位,(i)(i-mid)的其他位是一样的,贡献相同),所以我们可以知道:

    [b'y[i]=by[i]+bx[i-mid] ]

      同理,考虑(b'x[i])是怎么得到的,肯定也是(bx[i])加上(sumlimits_{j=mid+1}^{r}f(change(i,j))a[j])得到的,从同样的角度来分析,会发现(b'x[i])在计算的时候,我们带进去考虑的不同的那位是((0,1)),得出来是(1),会影响奇偶性,所以那一大段sigma应该是等于计算(by[i+ mid])(f)值为(0)(a[j])的和,我们记(sumlimits_{i=mid+1}^{r}a[i]=sum),那么:

    [b'x[i]=bx[i]+sum-by[i+mid] ]

      于是乎顺推的问题就很愉快滴解决了(感觉有点像FFT?)
      
    ​  
      
      但是现在的问题是知道(b)来求(a)啊,也就是我们要从(b'x)(b'y)推回(bx)(by) ,所以我们把上面的两条式子稍微变一下:

    [egin{aligned} bx[i]&=frac{b'x[i]+b'y[i+mid]-sum}{2}\ by[i]&=b'y[i]-bx[i-mid] end{aligned} ]

      看起来不错,但是那个(sum)怎么办?

      我们再考虑一下(by[r])(也就是(by)部分的最后一位)的值是哪些(a)贡献过来的,因为(r)(2^k-1)的形式,所以写成二进制之后(不看前导零)都是(1),也就是从下面的更小的区间合并上来时,怎么算(1)的个数的奇偶性都不会有变化,也就是说([l,r])这个区间内的所有(a)都是对其有贡献的,即(by[r]=sumlimits_{i=l}^{r}a[i]),而(bx[mid])(也就是(bx)部分的最后一位)则是除了从右边数第(k)位是(0)外,其他位和(r)相同,稍微思考一下其推上来的过程可以发现(bx[mid]=sumlimits_{i=l}^{mid}a[i])

      那么我们的(sum=by[r]-bx[mid])就好了

      然后就一路推下去长度从(n)推到(1)就可以将(a)完整还原出来啦
      
      
      
      代码大概长这个样子,十分简短

      (题外话:听说这个其实跟FWT很像?然后写起来的循环跟FFT长得一样样的除了最外层逆过来枚举了)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    using namespace std;
    const int MAXN=(1<<20)+10;
    ll a[MAXN];
    int n,m,T;
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("a.in","r",stdin);
    #endif
    	scanf("%d",&T);
    	ll sum;
    	for (int o=1;o<=T;++o){
    		scanf("%d",&n);
    		n=1<<n;
    		for (int i=0;i<n;++i) scanf("%lld",a+i);
    		for (int len=n;len>=1;len>>=1){
    			for (int st=0;st<n;st+=len){
    				sum=a[st+len-1]-a[st+(len>>1)-1];
    				for (int i=0;i<(len>>1);++i){
    					a[st+i]=(a[st+i]+a[st+i+(len>>1)]-sum)/2;
    					a[st+i+(len>>1)]=a[st+i+(len>>1)]-a[st+i];
    				}
    			}
    		}
    		for (int i=0;i<n;++i)
    			printf("%lld ",a[i]);
    		printf("
    ");
    	}
    }
    
    
  • 相关阅读:
    uIP的ARP协议分析
    如何使函数不生成执行代码
    计算机网络基础
    [Hive
    [Hive
    [Hive
    [Hive
    [Hive
    [Hive
    [Hive
  • 原文地址:https://www.cnblogs.com/yoyoball/p/8747119.html
Copyright © 2020-2023  润新知