$\$ios
$Description$
已知一個 $N$ 元高次方程: $$ k_1x_1^{p_1}+k_2x_2^{p_2}+...+k_nx_n^{p_n}=0 $$ 要求全部的 $x_i$ 取值範圍爲$[1,m]$且爲整數,求方程的解數。git
- $n\le 6,m\le 150$
$\$學習
$Solution$
發現 $150^6$ 複雜度爆炸,天然能想到折半搜。url
先搜前一半的全部可能的答案,存進哈希表裏,而後搜後一半的答案,在哈希表裏查相反數,若是存在就累加上個數。spa
而後 $map$ 就被卡 $T$ 了。其實這篇題解是哈希表學習筆記.......net
哈希表能夠理解爲一種相似多頭鏈表的結構。當答案很大可是答案的個數並非不少的時候選擇。code
每次獲得一個答案先將他縮小在$[1,mod]$範圍內,而後查詢這個值是否有存儲過,若是有就累加計數器。ip
若是沒有的話操做就頗有意思了。考慮到可能會有多個數通過模運算獲得的答案相同,因此不能直接在模運算所得答案處存儲這個數,而要像鄰接表同樣,由這個答案向真正的數連一條邊,邊權就是個數。get
而後查值得時候操做就和遍歷鄰接表同樣了。由於模數選擇質數,因此獲得的答案分佈仍是很均勻的,單次查詢和累加複雜度都接近$\text O(1)$。string
$\$
$Code$
#include<map> #include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define R register #define gc getchar #define mod 6893911 using namespace std; inline int rd(){ int x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } int n,m,t[10],k[10],ans; struct hashtable{ int hd[mod+2],tot; struct edge{int w,to,nxt;}e[4000000]; inline void add(int u,int v){ e[++tot].to=v; e[tot].w=1; e[tot].nxt=hd[u]; hd[u]=tot; } inline int find(int x){ int tmp=(x%mod+mod)%mod; if(!hd[tmp]) return -1; for(R int i=hd[tmp],v;i;i=e[i].nxt) if((v=e[i].to)==x) return e[i].w; return -1; } inline void insert(int x){ int tmp=(x%mod+mod)%mod; if(!hd[tmp]) add(tmp,x); else{ for(R int i=hd[tmp],v;i;i=e[i].nxt) if((v=e[i].to)==x){++e[i].w;return;} add(tmp,x); } } }s; inline int qpow(int x,int t){ int res=1; while(t){ if(t&1) res*=x; x*=x; t>>=1; } return res; } void dfsl(int p,int sum){ if(p>n/2){s.insert(sum);return;} for(R int i=1;i<=m;++i) dfsl(p+1,sum+k[p]*qpow(i,t[p])); } void dfsr(int p,int sum){ if(p>n){ int tmp=s.find(-sum); if(tmp>0) ans+=tmp; return; } for(R int i=1;i<=m;++i) dfsr(p+1,sum+k[p]*qpow(i,t[p])); } int main(){ n=rd(); m=rd(); for(R int i=1;i<=n;++i) k[i]=rd(),t[i]=rd(); dfsl(1,0); dfsr(n/2+1,0); printf("%d\n",ans); return 0; }