zoukankan      html  css  js  c++  java
  • 洛谷 P4106 / bzoj 3614 [ HEOI 2014 ] 逻辑翻译 —— 思路+递归

    题目:https://www.luogu.org/problemnew/show/P4106

    https://www.lydsy.com/JudgeOnline/problem.php?id=3614

    从很小的情况考虑,看题面上的样例:

    x1=+1 x2=+1  0

    x1=+1 x2=-1  1

    x1=-1 x2=+1  2

    x1=-1 x2=-1  3

    手算的做法应该是设原式为 a0 + a1x1 + a2x2 + a3x1x2

    通过加减,把关于 x2,x1 的项逐渐消去,得到常数项后再回代,逐个得出答案;

    然后我们发现加减的两个式子的位置也是有规律的,如果把-1看作0,+1看作1,式子的系数从小到大排序:

    x1=-1 x2=-1 3    —— 1

    x1=-1 x2=+1 2    —— 2

    x1=+1 x2=-1 1    —— 3

    x1=+1 x2=+1 0    —— 4

    12相加得到 x1 = -1 时无 x2 的值(+a0),34相加得到 x1 = 1 时无 x2 的值(+a0);

    21相减得到 x2 = 1 时无 x1 的值(-a3),43相减得到 x2 = 1 时无 x1 的值(+a3);

    两个相加的结果再相加得出a0,相减得出a1,两个相减的结果同理;

    可以发现,这样做其实就是把问题的规模变成了一半,可以递归求解;

    但空间很小,为了不递归,可以仿照FFT,FWT等,通过位置安排来直接循环做;

    实际上,最初的相邻两位置加减,让 xn 的有无与该位置末位的 0/1 对应;

    以此类推,如果倍增加减,则每个 xi  的有无就和该位置的第 i 位的 0/1 对应;

    “xi 的有无”意思是这个值中关于 xi 的项还是否存在;

    所以最后答案的 xi 情况就和其位置上的 0/1 分布情况一样,像FWT一样跳着加减就能得到;

    过程中不要除以2,最后输出时把 2^n 放在分母上;

    递归输出即可按照字典序,详见代码囧

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef double db;
    int const xn=(1<<20);
    int n,lim,f[xn];
    char dc[25];
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    void get()
    {
      scanf("%s",dc); int t=0; db ff;
      for(int j=0;j<n;j++){t<<=1; if(dc[j]=='+')t|=1;}
      scanf("%lf",&ff); 
      if(ff>0)f[t]=(int)(ff*100+0.5);
      else f[t]=(int)(ff*100-0.5);//!
    }
    void print(int x,int s)
    {
      if(x==0)return; if(x<0)putchar('-'),x=-x;//
      int t=(100<<n),g=gcd(x,t);//t:100*(/2)^n
      if(g==t)printf("%d",x/g); else printf("%d/%d",x/g,t/g);
      if(!s){puts(""); return;} putchar(' ');
      for(int i=n-1,nw=1;i>=0;i--,nw++)if(s&(1<<i))printf("x%d",nw);
      puts("");
    }
    void work(int nw,int s)
    {
      int t=((s<<1|1)<<(n-nw));
      print(f[t],t); if(nw==n)return;
      work(nw+1,(s<<1)|1);
      work(nw+1,(s<<1));
    }
    int main()
    {
      scanf("%d",&n); lim=(1<<n);
      for(int i=1;i<=lim;i++)get();
      for(int i=0;i<n;i++)
        {
          int t=(1<<i),d=(t<<1);
          for(int j=0;j<lim;j+=d)
        for(int k=0;k<t;k++)
          {
            int x=f[j+k],y=f[j+t+k];
            f[j+k]=x+y; f[j+t+k]=y-x;
          }
        }
      print(f[0],0); work(1,0);
      return 0;
    }
  • 相关阅读:
    自我学习笔记08
    自我学习笔记07
    自我学习笔记06
    自我学习笔记05
    自我学习笔记04
    自我学习笔记03
    自我学习笔记02
    WebApi HttpUtils
    Android 正则表达式,Pattern,Matcher基本使用
    Andriod项目结构
  • 原文地址:https://www.cnblogs.com/Zinn/p/10101198.html
Copyright © 2011-2022 走看看