1. 程式人生 > >[BZOJ3751][NOIP2014]解方程(數學相關+亂搞)

[BZOJ3751][NOIP2014]解方程(數學相關+亂搞)

題目描述

傳送門

題解

顯然若f(n)0(modp),則f(n+p)0(modp)
所以我們可以選上幾個質數,然後check出0-p-1之內的答案,然後由這些答案推出1-m內的答案。選上5個質數就差不多了。(抄的hzwer的質數
不過這樣的話跑得奇慢無比。算算時間複雜度應該是有一坨常數。
但是TA學長有一個我覺得更厲害並且可以證明時間複雜度的做法:首先選一個小質數p,然後處理出來0-p-1內的答案,然後推出1-m內的答案。然後再選一個大質數P,用來check所有合法的答案。
由拉格朗日定理,在0-p-1內至多隻有n個解,這樣的話不合法的答案至多會被check n 個。那麼時間複雜度可以估算為O

(np+n2mp)=O(n(p+nmp))其實是個對勾函式,當p=nm時有最小值,即O(2nnm)2106.

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 1000005

int n,m,ans,aa[105],mi[105];
char a[105][10005];
bool wtf[N],is[100005];
int Mod[5]={11261,19997,22877,21893,14843};

int calc(int id,int
mod) { int len=strlen(a[id]),A=0,st=0; bool flag=false; if (a[id][0]=='-') flag=true,st=1; for (int i=st;i<len;++i) A=(A*10+a[id][i]-'0')%mod; return (!flag)?A:-A; } int main() { scanf("%d%d\n",&n,&m); for (int i=0;i<=n;++i) scanf("%s",a[i]); for (int
i=1;i<=m;++i) wtf[i]=1; for (int k=0;k<5;++k) { for (int x=0;x<Mod[k];++x) is[x]=1; for (int i=0;i<=n;++i) aa[i]=calc(i,Mod[k]); for (int x=0;x<Mod[k];++x) { mi[0]=1;for(int i=1;i<=n;++i) mi[i]=mi[i-1]*x%Mod[k]; ans=0; for (int i=0;i<=n;++i) ans=(ans+aa[i]*mi[i]%Mod[k])%Mod[k]; if (ans) is[x]=0; } for (int x=1;x<=m;++x) if (!is[x%Mod[k]]) wtf[x]=0; } ans=0; for (int i=1;i<=m;++i) if (wtf[i]) ans++; printf("%d\n",ans); for (int i=1;i<=m;++i) if (wtf[i]) printf("%d\n",i); }

總結

①遇到這種題各種奇技淫巧還是要想一想的。