$\$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; }