专栏文章

调了一下午的一点心得……

P2312题解参与者 8已保存评论 12

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
12 条
当前快照
1 份
快照标识符
@miqfwvit
此快照首次捕获于
2025/12/04 04:09
3 个月前
此快照最后确认于
2025/12/04 04:09
3 个月前
查看原文

题外话

DX3906_ourstar(下文简称 DX)被本题卡了一下午,所以决定发篇题解纪念一下。
DX 的专栏被禁了,所以用的小号。

题意

求下面这个 11nn 次方程的不大于 mm 正整数解:
a0+a1x+a2x2++anxn=0a_0+a_1x+a_2x^2+\cdots+a_nx^n=0

思考

可能很多同学看到这里就跃跃欲试了:直接套求根公式!
很遗憾,高次方程没有求根公式。
看来要另辟蹊径了。我们可以先对等号左边的式子进行化简、变形,看看能否有所帮助。为表述方便,我们将它简写为 f(x)f(x),而我们的任务就是确保它为零。
不妨尝试提公因式,一次提出一个 xx。则有:
{} & f(x)\\ = & a_0+a_1x+a_2x^2+\cdots+a_nx^n\\ = & a_0+x(a_1+a_2x+a_3x^2+\cdots+a_nx^{n-1})\\ = & a_0+x(a_1+x(a_2+a_3x+a_4x^2\cdots+a_nx^{n-2}))\\ = & a_0+x(a_1+x(a_2+x(a_3+a_4x+a_5x^2+\cdots+a_nx^{n-3})))\\ = & \cdots\\ = & a_0+x(a_1+x(a_2+\cdots+x(a_{n-1}+a_nx)))\\ = & 0 \end{aligned}$$ **而这,就是我们解决本题的关键所在。** 既然 $f(x)=0$,那么就有下面的等式: $$f(x) \bmod p=0$$ 其中,$p$ 为一个很大的质数。显然,这个式子成立时,$f(x)$ 不一定为 $0$。它可以为 $2p,3p,4p\cdots$但感性认知一下,只要 $p$ 的取值恰当,那 $f(x)$ 就有极大的可能是 $0$,足够我们通过此题。 ## 代码 接下来,我们就需要结合上面推出的等式,用代码来表示 $f(x) \bmod p$ 了。试看下面这段代码: ``` ll sum=0;//十年 OI 一场空,______见祖宗。 for(int i=n;i>=1;--i){ sum=((a[i]+sum)*x)%p;//也就是我们推出的式子。 } sum=(a[0]+sum)%p; ``` 其中,`ll` 即为 `long long`,`sum` 即为等号左边的运算结果。把这段代码封装到一个 `bool` 型的函数里,便可用于判断某个 $x$ 的值是否可令 $f(x) \bmod p$ 为 $0$,也就是像下面这样: ``` inline bool check(int x){ /*这部分代码略*/ return sum==0; } ``` 那么剩下的工作就非常简单了。$m$ 并不是一个非常大的数,其取值范围允许我们一一枚举 $x \in [1,m]$。我们只需要挨个判断,最后进行输出即可。 多说无益,直接上完整代码! ``` #include<iostream> #include<queue> #define ll long long #define p 998244353377//学长说这是一个很棒的模数,不知道为甚,有巨佬能解答一下吗。 using namespace std; namespace OIfast{//一个快读快写的板子,曾被 @jerry1717 吐槽慢的一批 inline ll read(){ int n=0,f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1; c=getchar(); } while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar(); return n*f; } inline void print(ll n){ if(n<0)putchar('-'),n=-n; if(n>=10)print(n/10); putchar(n%10+'0'); return ; } inline void write(ll n,char c){ print(n),putchar(c); return ; } }using namespace OIfast; const int N=1e6+5; int n,m; ll a[N]; queue<ll> q; inline bool check(int x){ ll sum=0; for(int i=n;i>=1;--i)sum=((a[i]+sum)*x)%p; sum=(a[0]+sum)%p; return sum==0; } signed main(){ n=read(),m=read(); for(int i=0;i<=n;++i)a[i]=read(); for(int x=1;x<=m;++x){//枚举解。 if(check(x))q.push(x);//成立,则入队。 } write(q.size(),'\n');//使用队列就无需单独记录解的数量。 while(!q.empty())write(q.front(),'\n'),q.pop();//最喜欢用队列了。 return 0; } ``` 然后就可以愉快地 AC 了——[吗?](https://www.luogu.com.cn/record/list?pid=P2312&user=1268524&status=14&page=1) ## 敲黑板——几个坑点 ~~下面的抽象错误卡了 DX 一整个下午,真心希望同学们不要重蹈覆辙。~~ ### 坑点一:`long long` 开了,但没完全开 注意 Ln10:`int n=0,f=1;`,这是 DX 快读打魔怔了,不看数据规模的结果。可以发现,这里的 $n$ 开的是 `int`,稳炸。 修改措施:把 `int` 改成 `ll` 即可。 ### 坑点二:开了 `long long` 也并非不会炸 注意数据规模:$|a_i|\le10^{10000}$,再来看看快读里,开的不过是普通的 `long long`,稳炸。 我们又不可能再写一个高精吧…… 但我们可以更充分地利用模数。我们可以把 Ln16 后面加上这句话:`n%=p`。这就确保了 $|n|\le998244353377$,不会再爆炸。 ## 总结 本题恶狠狠地给 DX 上了一课。这同时也告诫大家注意细节,不要忽略任何一个可能出错的地方!

评论

12 条评论,欢迎与作者交流。

正在加载评论...