洛谷 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;
}

 

转载于:https://www.cnblogs.com/Zinn/p/10101198.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值