uoj20 解方程 数学

链接:http://uoj.ac/problem/20

题意:求出$a_0+a_1x+a_2x^2+...+a_nx^n=0$在$[1,m]$之间的所有整数根。

肯定有很多人想要直接枚举$[1,m]$之间所有的整数来进行暴力判断……坦白地讲我最早也是这么想的……直到看见了数据范围:$|a_i| \le 10^{10000}$……

我能怎么办我也很绝望啊.jpg

于是开始满$UOJ$找题解……挨个看完之后只能用劼司机语录表示我自己的心情了:给$vfk$、鏼鏼鏼、$Picks$、$ydc$挨个跪烂……

首先这么大的系数,我们肯定要在一个剩余系下面进行操作……不然根本算不了……然后大概就可以暴力枚举剩余系中每个数判解了……

但是问题来了:取膜太慢。因此,整个程序的一大难点就在于怎么样减少运行时间同时保证正确性。这几个人一人提出了一种方法:

$Picks$:任何一个$n$次多项式系数数列$n+1$次差分到最后都会变成$0$,那么直接补够$0$,预处理出每一层差分数列第一个值,然后每次线性递推,将乘法变为加法来减少常数。

$ydc$:容易证明任何一个根都有$x|a_0$,于是每个$p^x$,$p$为素数且不能被$a_0$整除的数的倍数都不是答案,然后问题转化为某个素数是否能把$a_0$整除,这个东西可以通过压$10^{18}$乱搞减少压力,然后瓶颈就在于判因子,我们就只预处理$10^5$以内的数,再大现场判定即可。

鏼鏼鏼:直接用$BSGS$的思想……拿出几个大小在$\sqrt{m}$左右的数并在这些剩余系之下进行操作,可以证明这样的正确率是相同的但时间复杂度却优化掉了一个$\sqrt{m}$……

我看了这么多之后还是决定用$vfk$的低端方法……

首先我们可以发现整个程序常数全在取膜上……既然如此我们就找一个取膜可以不用膜运算的素数好了!比如说:$2147483647$!

因为$a \cdot 2^{31} + b \equiv a + b \pmod{2^{31} - 1}$,所以$x$和$(x & 0x7fffffff) + (x >> 31)$在膜$2147483647$下面是同余的!

那么我们就直接用位运算代替取膜就好啦!这样可以使程序时间缩短为原来的三分之一!暴力判断完成一圈之后再随便找一个素数验证一下所有答案,就$OK$啦!

说得轻巧……实际上还是有一些问题的……我刚开始做的时候连续爆$70$……调了一下午……最后找了$vfk$自己写的程序对比了一下,震惊地发现$vfk$在膜完$2147483647$之后只是再取了一个大素数,而我则按照鏼鏼鏼的做法找了五个小素数……改了就过了……身败名裂.jpg……

大视野评测机太慢了这个方法在别的地方全过在这就$TLE$……

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 using namespace std;
 6 const int maxm=(int)1e6+5,maxl=10005,maxn=105;
 7 const int mod[]={2147483647,999946333};
 8 int n,m,a[maxn][maxl],anssum,isfu[maxn];unsigned long long amod[maxn];bool notans[maxm];
 9 char s[maxm];
10 unsigned long long check(unsigned long long val)
11 {
12     unsigned long long y=0;
13     for(int i=n;~i;i--)
14     {
15         y=y*val+amod[i];
16         y=(y&0x7fffffff)+(y>>31);
17     }
18     y%=0x7fffffff;
19     return y;
20 }
21 bool check(int val,int module)
22 {
23     val%=module;unsigned long long y=0;
24     for(int i=n;~i;i--)
25     {
26         y=y*val+amod[i];
27         y%=module;
28     }
29     y%=module;return y;
30 }
31 int haha()
32 {
33     // freopen("equationa.in","r",stdin);
34     // freopen("equationa.out","w",stdout);
35     scanf("%d%d",&n,&m);anssum=m;
36     for(int i=0;i<=n;i++)
37     {
38         scanf("%s",s+1);int len=strlen(s+1);
39         for(int j=1;j<=len;j++)
40         {
41             if(s[j]=='-'){isfu[i]=1;continue;}
42             if(s[j]>='0'&&s[j]<='9')a[i][++a[i][0]]=s[j]-'0';
43         }
44     }
45     for(int i=0;i<=n;i++)
46     {
47         amod[i]=0;
48         for(int j=1;j<=a[i][0];j++)
49         {
50             amod[i]=amod[i]*10+a[i][j];
51             amod[i]%=0x7fffffff;
52         }
53         amod[i]=amod[i]%0x7fffffff;
54         if(isfu[i]&&amod[i])amod[i]=0x7fffffff-amod[i];
55     }
56     for(int i=1;i<=m;i++)
57         if(check(i))anssum--,notans[i]=1;
58     for(int t=1;t<2;t++)
59     {
60         for(int i=0;i<=n;i++)
61         {
62             amod[i]=0;
63             for(int j=1;j<=a[i][0];j++)
64             {
65                 amod[i]=amod[i]*10+a[i][j];
66                 amod[i]%=mod[t];
67             }
68             amod[i]%=mod[t];
69             if(isfu[i]&&amod[i])amod[i]=mod[t]-amod[i];
70         }
71         for(int i=1;i<=m;i++)
72         {
73             if(notans[i])continue;
74             if(notans[i%mod[t]]){anssum--,notans[i]=1;continue;}
75             if(check(i,mod[t]))anssum--,notans[i]=1;
76         }
77     }
78     printf("%d\n",anssum);
79     for(int i=1;i<=m;i++)
80         if(!notans[i])printf("%d\n",i);
81 }
82 int sb=haha();
83 int main(){;}
uoj20

 

posted @ 2017-10-24 19:48  ccc000111  阅读(277)  评论(0编辑  收藏  举报