zoukankan      html  css  js  c++  java
  • P4106 [HEOI2014]逻辑翻译

    题目链接

    题意分析

    这道题就是上来一个多项式

    [f(x_1,x_2,x_3)=a_0+a_1x_1+a_2x_2+a_3x_3+a_4x_1x_2+a_5x_1x_3+a_6x_2x_3+a_7x_1x_2x_3 ]

    然后通过对于(x_1,x_2,x_3)不同的取值对应的函数值让我们得到这个多项式的系数

    首先 你可以暴力高斯消元 复杂度优美到飞起

    其实对于这道题的话 如果你们有FFT或者FWT的经验的话 这道题的思路应该是比较应该是比较好想的反正本蒟蒻是没有

    上述多项式转化一下

    [f(x_1,x_2,x_3)=a_0+a_2x_2+a_3x_3+a_6x_2x_3+x_1(a_1+a_4x_2+a_5x_3+a_7x_2x_3) ]

    然后的话 我们就可以分治了 但是对于左右应该怎么分治?

    我们考虑一下 对于(x_1)存在(1)以及(-1)两种取值

    那么取值不同的话就是(A)以及(B)

    然后我们可以是左半部分(frac{A+B}{2}) 右半部分(frac{A-B}{2})

    左半部分就是计算(x_2,x_3,x_2x_3)的系数以及常数项

    右半部分就是计算(x_1,x_1x_2,x_1x_3,x_1x_2x_3)的系数

    这大概就是答题的思路

    然后就是这道题一些比较有意思坑人的地方

    1.由于这道题的答案都是使用分数的形式输出 所以我们需要维护一个结构体进行分数运算

    2.这道题的输出比较呛人 所以我们考虑直接dfs输出

    3.一开始对于小数x的话 由于ta最多不超过2位小数 所以我们可以转化为分子round(100x) 分母(x) (round()函数是四舍五入的意思)

    具体的实现我们还是看代码实现

    CODE:

    #include<bits/stdc++.h>
    #define N 22
    #define M 1508611
    using namespace std;
    char s[N];
    long long gcd(long long x,long long y){return y ? gcd(y,x%y):x;}
    struct Node
    {//我用于维护分数的结构体
    	long long up,dow;//分子分母如果不使用long long的话中间结果会炸
    	friend Node operator +(const Node &A,const Node &B)
    	{
    		Node tmp;long long x;
    		tmp.up=A.up*B.dow+A.dow*B.up;
    		tmp.dow=A.dow*B.dow;
    		x=gcd((tmp.up>0 ? tmp.up:-tmp.up),(tmp.dow>0 ? tmp.dow:-tmp.dow));
    		if(tmp.dow<0) tmp.dow=-tmp.dow,tmp.up=-tmp.up;
    		return (Node){tmp.up/x,tmp.dow/x};
    	}
    	friend Node operator -(const Node &A,const Node &B)
    	{
    		Node tmp;long long  x;
    		tmp.up=A.up*B.dow-A.dow*B.up;
    		tmp.dow=A.dow*B.dow;
    		x=gcd((tmp.up>0 ? tmp.up:-tmp.up),(tmp.dow>0 ? tmp.dow:-tmp.dow));
    		if(tmp.dow<0) tmp.dow=-tmp.dow,tmp.up=-tmp.up;
    		return (Node){tmp.up/x,tmp.dow/x};
    	}
    	friend Node operator /(const Node &A,int B)
    	{
    		Node tmp;long long  x;
    		tmp.up=A.up;tmp.dow=A.dow*B;
    		x=gcd((tmp.up>0 ? tmp.up:-tmp.up),(tmp.dow>0 ? tmp.dow:-tmp.dow));
    		if(tmp.dow<0) tmp.dow=-tmp.dow,tmp.up=-tmp.up;
    		return (Node){tmp.up/x,tmp.dow/x};
    	}
    }num[M];
    int n;
    bool vis[M];
    void print(int now)
    {
    	if(vis[now]||num[now].up==0) return;
    	//这里为了防止重复输出 我使用了一个标记数组
    	//同时分子为0的话 就是本身值为0 那么就不用输出了 
    	vis[now]=1;
    	Node x=num[now];
    	if(x.dow==1) printf("%lld ",x.up);
    	else printf("%lld/%lld ",x.up,x.dow);
    	for(int i=0;i<n;++i)
    	if(now&(1<<i)) printf("x%d",i+1);
    	puts("");
    }
    void dfs_ans(int tox,int nowhave)
    {//我们是从低位到高位递归 对应的就是1,2,3,...,n 
    //nowhave就是当前枚举的位
    //tox就是已经枚举了的低位状态
    	if(nowhave==n) return;
    	for(int i=0;i<(1<<(n-nowhave-1));++i)
    	{//所以当前就是在枚举高位的状态   
    		int cdy=(i<<(nowhave+1))|(tox)|(1<<nowhave);//高位的状态|已经枚举了的低位状态|当前位是0还是1
    		int wzy=(i<<(nowhave+1))|(tox);
    		Node tmp=num[cdy];
    //这里的话我一直都不太好理解 后来才稍稍有些眉目
    //我们还是考虑一下 对于题目给出的2^n个状态 其他条件分别取了x1=1以及x1=-1时对应的函数值对应着什么 
    		num[cdy]=(tmp-num[wzy])/2;
    		num[wzy]=(tmp+num[wzy])/2;
    //我们按照上面的做法 消掉之后
    //就是等于当前这一位取0时(x2,x3,x2x3,常数项) 不同状态对应的不同函数值
    //以及当前这一位取1时(x1,x1x2,x1x3,x1x2x3) 不同状态对应的不同函数值
    //最后也就是相当于是axi+C 也就是a+C以及-a+C 从而得到我们要的答案
    	}
    	dfs_ans(tox,nowhave+1);
    	dfs_ans(tox|(1<<nowhave),nowhave+1);
    //然后我们分别用取和不取对应的状态 向高位前进
    }
    void print_ans(int tox,int nowhave)
    {
    	if(nowhave==n)
    	{//最后到了的话就是直接输出
    		print(tox);
    		return;
    	} 
    	print(tox);//当前这一位没有的话自然是字典序优先级最高的 所以我们优先输出
    	print_ans((tox|(1<<nowhave)),nowhave+1);//然后的话就是低位先有低位优先
    	print_ans(tox,nowhave+1);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=0;i<(1<<n);++i)
    	{
    		int now=0;double tmp;
    		scanf("%s %lf",s,&tmp);
    		for(int j=0;j<n;++j)
    		if(s[j]=='+') now+=(1<<j); 
    		//首先 一开始的读入我们直接转化为对应的二进制状态
    		num[now].up=round(tmp*100.0);
    		num[now].dow=100;
    		int x=gcd((num[now].up>0 ? num[now].up:-num[now].up),(num[now].dow>0 ? num[now].dow:-num[now].dow));
    		if(num[now].dow<0) num[now].up=-num[now].up,num[now].dow=-num[now].dow;
    		num[now].up/=x;num[now].dow/=x;
    		//然后是对于每一种状态对应函数值从小数维护成分数
    	}
    	dfs_ans(0,0);	
    	print_ans(0,0);
    	return 0;
    } 
    
  • 相关阅读:
    固定sql语句传参批量查询数据库脚本
    超多行数据纵向编辑
    takes 3 positional arguments but 4 were given错误
    使用PMD进行代码审查(转)
    WebADI应用到Office 2016 64-bit
    SVN 提交代码时强制加入注释内容
    DOCKER初体验
    "make_path" is not exported by the File::Path modul
    perl 调用shell脚本
    scp 上传文件到多个服务器节点
  • 原文地址:https://www.cnblogs.com/LovToLZX/p/14100482.html
Copyright © 2011-2022 走看看