11 2

今天不太行 剛了很久T3 沒考慮到一種很是特殊的狀況而後我掛了 而後我仔細思索了一下 獲得了正解。今天得分138 T1 50 沒有好好思考怎麼構造 緣由沒時間了 T2 38 感受有點不可作 果斷爆搜 構造 隨機化 騙分。ios

T3 感受最好作的題目得分50 數組開小了 少討論一種重要的狀況 而後成功掛掉。c++

先從T3 提及。數組

這道題 比較有意思 我以爲能夠寫可是實際上我沒有思考周全 有點矇蔽了早上 忽然傻了。(可能昨天晚上3點睡的緣故。/cyide

首先就考慮爆搜 而後發現這個最多的gcd個數實際上是包含最多質因子個數的 由於每次我均可以減少一個gcd來保證gcd的不一樣。優化

那麼此時 個人漏洞就是我質因數最多的那個數且<=n 其必然是2的若干次冪 由於若是是3或者是其餘的話能夠直接利用2來替換並且還能夠更小。spa

可是我忽略了一點 還有一個數字也多是答案 2^(y-1)*3 這個數字包含的質因子個數和2^y的個數相同當同時<=n時均可以放到隊首。3d

咱們討論一下如何獲得方案這個我想了很久很久 我沒想到dp 直接算組合數了複雜度logn 是正解的複雜度 可是 少討論了一種狀況而後歇菜了。code

先考慮dp吧 f[i][j][k] 表示 前i個數字此時gcd是2^j*3^k的方案數 每一次要麼不變要麼少一個2 要麼少一個3 隨便轉移一下便可。blog

 //#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=1000010;
ll n;
ll mx,mx1;
ll c[21][2];//c[i][j]表示 n之中2^j*3^k的個數
ll f[MAXN][21][2];//f[i][j][k] 表示前i個數子填事後 此時gcd爲2^j*3^k的方案數
signed main()
{
    freopen("1.in","r",stdin);
    n=read();
    if(n==1||n==2){puts("1");return 0;}
    ll w=1;
    for(ll i=1;;++i)
    {
        w=w<<1;
        if(w>n){mx=i;break;}
    }
    --mx;
    if((1<<(mx-1))*3<=n)++mx1;
    f[1][mx][0]=1;
    if(mx1)f[1][mx-1][mx1]=1;
    for(ll i=0;i<=mx;++i)
        for(ll j=0;j<=mx1;++j)
        {
            w=(1<<i)*(j?3:1);
            c[i][j]=n/w;
        }
    for(ll i=1;i<n;++i)
    {
        for(ll j=0;j<=mx;++j)
        {
            for(ll k=0;k<=mx1;++k)
            {
                if(!f[i][j][k])continue;
                if(c[j][k]-i>=1)f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k]*(c[j][k]-i)%mod)%mod;
                if(j-1>=0)f[i+1][j-1][k]=(f[i+1][j-1][k]+f[i][j][k]*(c[j-1][k]-c[j][k])%mod)%mod;
                if(k-1>=0)f[i+1][j][k-1]=(f[i+1][j][k-1]+f[i][j][k]*(c[j][k-1]-c[j][k])%mod)%mod;
            }
        }
    }
    printf("%lld\n",f[n][0][0]);
    return 0;
}
View Code

80分到手我這腦子就是sb了。ip

優化也很簡單 考慮組合意義 由於咱們每一次不變的話就會帶來O(n)*logn的複雜度 可是 減小的話最多logn次 因此能夠考慮怎麼把不變的狀況直接 統計到答案裏面。

其實 咱們能夠按位考慮 早上我就是按位考慮的 對於logn個數字咱們強制欽定 其他的數字安排一下咱們的複雜度就降到logn了。

其實 對於某個位置上的數字咱們強制留下一個數字 剩下能放到數字實際上是,設當前還能用的數字的位置是k 那麼 對答案的貢獻實際上是 k*(k-1)*(k-2)...

這個東西。隨便放的意思 而後複雜度就被降到logn了 須要求一下階乘 和 階乘的逆元便可。

先放考場50分相似的思想代碼(就考慮一種狀況 直接遞推作的。並且數組也開小了。下面代碼只能處理只有一個2^y的狀況。

 //#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
/*
const int MAXN=20;
int a[MAXN],cnt,ans,maxx;
int vis[MAXN];
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline void dfs(int x)
{
    if(x==n+1)
    {
        int G=a[1];cnt=1;
        for(int i=2;i<=n;++i)
        {
            int g=gcd(G,a[i]);
            if(g==G)continue;
            G=g;++cnt;
        }
        if(cnt==maxx)++ans;
        else if(cnt>maxx)maxx=cnt,ans=1;
        return;
    }
    for(int i=1;i<=n;++i)
    {
        if(vis[i])continue;
        a[x]=i;
        vis[i]=1;
        dfs(x+1);
        vis[i]=0;
    }
}*/
const int MAXN=1000010;
int n;
ll fac[MAXN],ans=1,sum;
ll inv[MAXN];
inline ll fast_pow(ll b,ll p)
{
    ll cnt=1;
    while(p)
    {
        if(p&1)cnt=cnt*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return cnt;
}
int main()
{
    //freopen("1.in","r",stdin);
    freopen("shimakaze.in","r",stdin);
    freopen("shimakaze.out","w",stdout);
    n=read();
    if(n==1){puts("1");return 0;}
    if(n==2){puts("1");return 0;}
    if(n==3){puts("4");return 0;}
    if(n==4){puts("2");return 0;}
    if(n==5){puts("6");return 0;}
    if(n==6){puts("120");return 0;}
    if(n==7){puts("600");return 0;}
    if(n==8){puts("240");return 0;}
    if(n==9){puts("1440");return 0;}
    //if(n==12){puts("7015680");return 0;}
    //dfs(1);
    //printf("%d\n",ans);
    fac[0]=1;
    for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod;
    inv[n]=fast_pow(fac[n],mod-2);
    for(int i=n-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
    int w=1,flag;
    for(int i=1;;++i)
    {
        w=w<<1;
        if(w>n){flag=i;break;}
    }
    //cout<<flag<<endl;
    --flag;
    for(int i=flag;i>=0;--i)//填入2^若干次冪
    {
        int w=1<<i;
        int res=n/w-sum;
        int sum1=sum+res;
        ans=ans*res%mod;
        res--;//欽定位置
        ++sum;
        ans=ans*fac[n-sum]%mod*inv[n-sum-res]%mod;
        sum=sum1;
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

值得一提的是當 有3的狀況下咱們 必須使用dfs來完成遞推這個過程由於(有點強制性的意味了。不過不寫搜索過程我以爲很難完成。

還有一點值得一提的是 這個搜索的複雜度並不是logn的 而是但其上界是logn*logn 這個東西能夠被證實 畫一下圖就知道了。

 //#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=10000010;
ll n;
ll fac[MAXN],ans,sum,mx,flag;
ll inv[MAXN];
inline ll fast_pow(ll b,ll p)
{
    ll cnt=1;
    while(p)
    {
        if(p&1)cnt=cnt*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return cnt;
}
inline void dfs(ll i,ll j,ll v,ll sum)
{
    if(!i&&!j)
    {
        ans=(ans+v)%mod;
        return;
    }
    if(i)//扔掉一個2
    {
        ll w=(1<<(i-1))*(j?3:1);
        ll ww=n/w-sum;//當前數字大小 還剩下的個數
        dfs(i-1,j,v*ww%mod*fac[n-sum-1]%mod*inv[n-sum-ww]%mod,sum+ww);
    }
    if(j)
    {
        ll w=(1<<i);
        ll ww=n/w-sum;
        dfs(i,j-1,v*ww%mod*fac[n-sum-1]%mod*inv[n-sum-ww]%mod,sum+ww);
    }
}
signed main()
{
    freopen("1.in","r",stdin);
    //freopen("shimakaze.in","r",stdin);
    //freopen("shimakaze.out","w",stdout);
    n=read();
    //dfs(1);
    //printf("%d\n",ans);
    fac[0]=1;
    for(ll i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod;
    inv[n]=fast_pow(fac[n],mod-2);
    for(ll i=n-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
    ll w=1;
    for(ll i=1;;++i)
    {
        w=w<<1;
        if(w>n){flag=i;break;}
    }
    //cout<<flag<<endl;
    --flag;
    if((1<<(flag-1))*3<=n)mx=1;
    dfs(flag,0,1,1);
    if(mx)dfs(flag-1,mx,1,1);
    printf("%lld\n",ans);
    return 0;
}
View Code

但相比階乘預處理的O(n) 來看仍是很小的 因此此題總複雜度爲O(n).

 

這道題就相對的有點意思了 數數題目的能力還須要增強Y。

仍是要求一個排列 使得 上述的式子的值最小 對於n<=10 爆搜便可。

對於樹是一條鏈 構造的話就很簡單了 考慮 拿出鏈的中點 考慮重心只有兩個的狀況 那麼此時重心之間的邊的兩邊兒子的數量必定相等 若是左邊的兒子的pi在左邊 那麼也必定存在一個右兒子的pi在右邊。

此時交換兩個pi 可使得代價更大 因此此時咱們發現左邊兒子的pi都在右邊 右邊兒子的pi都在左邊 那麼顯然 咱們發現了 對於左邊的pi 隨便交換答案不變右邊的同理。

因此此時方案數是 (n/2)!(n/2)!構造的話左邊輸出右邊的便可。一個比較好的作法 是 把鏈拉出來而後reversal一下而後再輸出。

考慮奇數的狀況 咱們仍是會發現若是左邊的pi在左邊且此時右邊的pi在右邊會變得不優 此時跟剛纔同樣 可是中間那個點如何處理?

考慮當前這個點和其餘點的 交換,答案不變且因此當前的方案是(n/2)!(n/2)!*n 構造和上述的方法同樣。

考慮一下正解。咱們對於一個排列的貢獻是 $\sum_{dep_i}+\sum_{dep_{pi}}-\sum_{LCA(i,p_i)}$

若是不是重心的話 i 和 pi必定會出現一棵子樹之中因此LCA 這個式子是不能等於0的因此選擇重心而後 當i和pi不在同一棵子樹中的時候 就能夠取0了。

先求方案數吧 當有兩個重心的時候這個時候的 很好討論了方案數顯然 ((n/2)!)^2

1個重心怎麼作?

相關文章
相關標籤/搜索