[排列组合 + 分段打表] 51nod 算法马拉松25 A. 二分答案
2017-05-28 22:39
555 查看
题意
题解
分析一下可得,如果二分最后要停在k位置,需要有一些位置上的值满足与m的一些大小关系,而且关系都是确定的。直接模拟一趟就可以得到:有num1个位置需要满足a[i]<=m,有num2的位置需要满足a[i]>m,其他位置显然可以随便乱放,因为根本就不会访问到它们。
这样就能直接写出答案的表达式了:
Pnum1m∗Pnum2n−m∗(n−num1−num2)!
问题来了,如何求大数(1e+9)阶乘模1e9+7的值呢?
由于数字范围和模数范围都很大,这个东西不好直接求,所以这里需要用到一些奇技淫巧,分段打表登场。
大概就是均匀的每隔一段打一个,然后算阶乘时找到表中最相邻的,然后暴力一步一步推即可。
然后复杂度就被强行除了一个数。又解锁了新姿势。
表不大,代码就直接贴了。
#include<cstdio> #include<algorithm> using namespace std; typedef long long LL; const LL MOD=1000000007,blk=5000000; const int lst[201]={1,974067448,682498929,598816162,491101308,586350670,76479948,463847391,723816384,172827403,67347853,407719831,27368307,606322308,625544428,1669644,199888908,534491822,888050723,884343068,927880474,112249297,281863274,770511792,661224977,935080803,623534362,797848181,970055531,232253360,261384175,659224434,195888993,509096183,66404266,785113347,547665832,996122673,109838563,34538816,933245637,911398531,724691727,114985663,368925948,464456846,268838846,938269070,136026497,564758715,112390913,167240465,135498044,889410460,217544623,996097969,419363534,607730875,500780548,651081062,668123525,563432246,128487469,318951960,30977140,93940075,522049725,559947225,309058615,624577416,386027524,716986954,189239124,179518046,148528617,529726489,940567523,473718967,917084264,105419548,429277690,937409008,996164327,418537348,358655417,775305697,568392357,392838702,780072518,596737944,462639908,843321396,275105629,432030917,909210595,321685608,99199382,5003231,703397904,863250534,733333339,180898306,97830135,864869025,608823837,46819124,256141983,491415383,141827977,688809790,696628828,457469634,637939935,502297454,811575797,48053248,848924691,26011548,131772368,271198437,724464507,510650790,272814771,564188856,326159309,44135644,456152084,816929577,903466878,218107212,92255682,196345098,769795511,407518072,373745190,147050765,606241871,666493603,825871994,561011609,957939114,765215899,435887178,201339230,852304035,469928208,663307737,414236650,375297772,211487466,217598709,676526196,624148346,439411911,671734977,644050694,624500515,715264908,748510389,819801784,203191898,340030191,423951674,331910086,629786193,341080135,672850561,684748812,814362881,60625018,823845496,175638827,116667533,163928347,256473217,393556719,627655552,687265514,245795606,36292292,586445753,953634340,172114298,260466949,193781724,756154604,778983779,49031023,83868974,954913,315103615,346966053,965785236,321900901,492741665,532702135,377329025,847645126,847549272,3258987,698611116}; LL n,m,K,num1,num2; LL Fact(LL x){ if(x<0) return 0; LL res=lst[x/blk]; for(LL i=(x/blk)*blk+1;i<=x;i++) res=(res*i)%MOD; return res; } LL power(LL a,LL b){ if(b==0) return 1; if(b&1) return (power(a,b-1)*a)%MOD; LL t=power(a,b/2); return (t*t)%MOD; } LL P(LL a,LL b){ if(a<0||b<0||a<b) return 0; return (Fact(a)*power(Fact(a-b),MOD-2))%MOD; } int main(){ freopen("51nod25A.in","r",stdin); freopen("51nod25A.out","w",stdout); scanf("%lld%lld%lld",&n,&m,&K); LL L=1,R=n,mid=(L+R)>>1; while(L<=R){ if (mid<=K) L=mid+1, num1++; // a[mid]<=m else R=mid-1, num2++; // a[mid]>m mid=(L+R)>>1; } printf("%lld\n",(P(m,num1)*P(n-m,num2)%MOD)*Fact(n-num1-num2)%MOD); return 0; }
相关文章推荐
- 算法马拉松25 二分答案(组合数新高度!!!无敌的降维)
- [方差+lucas定理] 51nod 算法马拉松25 D. 小Q的集合
- 51nod 算法马拉松30 A.函数【容斥】【组合数学】
- 51Nod 算法马拉松17 最好的排列 贪心求解加高精度
- 51nod 1799 二分答案 思维 + 分块打表
- 51nod 算法马拉松25
- 51nod 1799 二分答案 (打表)
- [递推] 51nod 算法马拉松25 B. 完美序列
- 51nod 1799 二分答案(分块打表+组合数)
- 一个排列、组合的生成算法 [zz]
- 一个排列组合算法---裂变算法
- 高效率的排列组合算法(java实现)
- C#实现排列组合算法
- 排列、组合及算法
- 求N选M的组合和求全排列的巧妙算法
- 排列组合问题的通用算法
- 高效率的排列组合算法
- 求N选M的组合和求全排列的巧妙算法 - 算法艺术 - Hello, busycai!
- 排列组合的一些算法
- 排列组合问题的通用算法