NOIP模拟题 2015.8.9 T3
这是破神蒯的一个sxbk的原题,在一个叫做Bayan的比赛的2014年shortcutB题
题面:有n个白球的和m个黑球,要求将他们排成一列使得每对异色球之间的人数之和最小,并求方案数模10^9+7。
n,m<=100w,T组数据,T<=100
输入格式:第一行一个T,然后T行每行两个n和m
输出格式:T行每行两个答案
Sample Input:
题解:做这套题睡了2h+,所以考的比较hehe,当然就算再给我2h+我也不一定做得出这个题。
这道题喜闻乐见的要推式子:
定义Sn代表n个人两两颜色相异的时候的答案,通过计算可得Sn=(n3-3*n2+2n)/6
先设n>=m,然后将考虑将m个黑球插进来,定义Ai代表第i个黑球插到了第Ai个白球的的后面,我们假设Ai单调不减
简单容斥可得:ans=Sn+m-Sn-Sm-所有的黑球对所有的白球对的贡献-所有的白球对所有的黑球对的贡献,
倒数第二个东西就是Σi=1 to m Ai*(n-Ai)
最后一个东西就是Σi=1 to m-1 Σj=i+1 to m Aj-Ai-1,然后这东西明显是可以拆出来的
最后两个东西相加得到:Σi=1 to m (n-2*i-m-1)2-[Ai-(n+2*i-m-1)/2]2
这东西系数是负的,所以后面那个和平方要最小。
所以我们分类讨论当n-m为奇数,Ai后面那个东西总是能取到0,因为(n+2*i-m-1)/2总是大于等于1,小于等于n-1。这样的话就可以直接算了,并且方案数是1
当n-m为偶数的时候,后面那个和平方最优取值应该是0.25,此时Ai有两个最优取值,并且由于i每次增大1,所以Ai取较大的那个,而Ai-1取较小的那个的时候也不会违反假设。所以答案还是可以直接算,然后方案数是2m,这个快速幂一下就好了
为了能过我们还要优化一下,前面那个m个常数的和平方是可以化简的,化简成:(3n2m-3m3-3m)/12
然后还有第一问是要用long double,输出用long long,后面的要取模。然后就没了
en窝不会LaTeX所以这些东西都看起来很丑请不要介意(好吧其实也没人看吧)
然后其实代码也很丑,这虽然是一般设定不过还是说一下好了
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; #define ll long long #define M 1000000007 long double ans;ll t,n,m; inline int Read() { int x=0;char y; do y=getchar(); while (y<'0'||y>'9'); do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9'); return x; } ll Quick_Power(ll x,int y) { ll z=1; while (y) { if (y&1) z=z*x%M; y >>= 1; x=x*x%M; } return z; } inline long double Calc(ll x) {return (x*x*x-3*x*x+2*x)/6;} inline long double calc() {long double x=n,y=m;return (3*x*x*y+y*y*y-y)/12;} inline void Solve1() {printf("%lld 1\n",(ll)(ans+0.5));} inline void Solve2() { ans+=0.25*m; printf("%lld %lld\n",(ll)(ans+0.5),Quick_Power(2,m)); } int main() { freopen("love.in","r",stdin);freopen("love.out","w",stdout); t=Read(); while (t--) { n=Read();m=Read(); if (n<m) swap(n,m); ans=Calc(n+m)-Calc(n)-Calc(m); ans-=calc(); if ((n-m)&1) Solve1();else Solve2(); } return 0; }