UVa1649/Gym-100729A Binomial coefficients 数学+二分

这道题真感觉没什么思路。。

通过看杨辉三角,一开始写了个暴搜,虽然有很多剪枝,也优化了很多,但还是果断TLE。。

附上TLE的代码:(心情复杂.jpg)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
using namespace std;
#define ll long long
typedef pair<ll,ll>pp;
const double pi=acos(-1.0);
const double eps=1e-9;
const int INF=0x3f3f3f3f;
const ll MOD=1e9+7ll;

ll m;
ll fac(ll m,ll n)
{
    ll ans=1;
    for(ll i=1;i<=m;i++)
    {
        ans*=n-i+1;
        ans/=i;
    }
    return ans;
}
pp p[1234567];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        cin>>m;
        if(m==2)
        {
            printf("1\n");
            printf("(2,1)\n");
        }
        else
        {
            ll ans=0;
            ll tol=0;
            memset(p,0,sizeof(p));
            vector<int>v[2];
            v[0].clear();v[1].clear();
            v[0].push_back(1);v[0].push_back(2);v[0].push_back(1);
            for(ll i=3;i<m;i++)
            {
                ll k=i/2;
                v[1].push_back(1);
                for(ll j=1;j<v[0].size();j++)
                    v[1].push_back(v[0][j-1]+v[0][j]);
                v[1].push_back(1);
                /*for(ll j=0;j<v[1].size();j++)
                    cout<<v[1][j]<<" ";
                cout<<endl;*/
                v[0].clear();
                for(ll j=0;j<v[1].size();j++)
                    v[0].push_back(v[1][j]);
                if(v[1][k]<m)
                {
                    v[1].clear();
                    continue;
                }
                for(ll j=1;j<=k;j++)
                {
                    if(v[1][j]==m)
                    {
                        p[++tol].first=i,p[tol].second=j;
                        p[++tol].first=i,p[tol].second=i-j;
                        break;
                    }
                    if(v[1][j]>m)
                        break;
                }
                v[1].clear();
            }
            printf("%lld\n",tol+(ll)2);
            for(int i=1;i<=tol;i++)
                printf("(%lld,%lld) ",p[i].first,p[i].second);
            printf("(%lld,%lld) ",m,(ll)1);
            printf("(%lld,%lld) \n",m,m-1);
        }
    }
    return 0;
}

之后听某大佬说,C(60,30)就已经到10^15次了Orz,所以k最大就是30,后来发现27就可以。

附上参考博客Orz:https://blog.csdn.net/u014800748/article/details/45424285

因为m非常大,枚举n就会十分困难,所以不如枚举k,二分n。

对于k来说,下界为2*k,上界为m。(n越大,C(n,k)就越大。实在想不通就举个例子试一下)

然后发现一个神奇的地方:求阶乘的函数fac里的那个循环,if判断不能少,不然会WA。。

我觉得这个很迷啊。。fac函数的原理主要是:C(n,k)=(n-k+1)/k*C(n,k-1),可以中间直接进行判断返回,即如果发现算到第i步时结果已经超过m了,说明n肯定不是解,返回一个m+1。按理说不加也可以,而且我试着变成判断if((ans/i*(n-i+1))>m),结果也WA了,很可能是爆了long long。。

附上AC代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define ll long long
typedef pair<ll,ll>pp;
const ll MOD=1e9+7ll;

ll m;
set<pp>ans;

ll fac(int k,ll n)
{
	ll ans=1;
	for(int i=1;i<=k;i++)
	{
        //如果发现算到第i步时结果已经超过m了,说明n肯定不是解,返回一个m+1
		if((ans/i)>(m/(n-i+1)))
			return m+1;
		ans*=(n-i+1);
		ans/=i;
	}
	return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        cin>>m;
        if(m==2)
        {
            printf("1\n");
            printf("(2,1)\n");
        }
        else
        {
            ans.clear();
            /*ans.insert(make_pair(m,1));
            ans.insert(make_pair(m,m-1));*/
            for(ll k=1;k<=27;k++)
            {
                ll l=2*k;
                ll r=m;
                while(l<=r)
                {
                    ll mid=(l+r)/(ll)2;
                    ll tmp=fac(k,mid);
                    if(tmp==m)
                    {
                        ans.insert(make_pair(mid,k));
                        if(mid==2*k)//注意!!
                            break;
                        ans.insert(make_pair(mid,mid-k));
                        break;
                    }
                    else if(tmp<m)
                        l=mid+1;
                    else
                        r=mid-1;
                }
            }
            ll len=ans.size();
            cout<<len<<endl;
            if(len==0)
                continue;
            pp p;
            for(int i=0;i<len;i++)
            {
                p=(*ans.begin());
                cout<<"("<<p.first<<","<<p.second<<") ";
                ans.erase(ans.begin());
            }
            printf("\n");
        }
    }
    return 0;
}

  我我我...我好菜啊啊啊啊55555...

猜你喜欢

转载自blog.csdn.net/Cc_Sonia/article/details/81913921
今日推荐