zoukankan      html  css  js  c++  java
  • 11 2

    今天不太行 刚了好久T3 没考虑到一种非常特殊的情况然后我挂了 然后我仔细思索了一下 得到了正解。今天得分138 T1 50 没有好好思考怎么构造 原因没时间了 T2 38 感觉有点不可做 果断爆搜 构造 随机化 骗分。

    T3 感觉最好做的题目得分50 数组开小了 少讨论一种重要的情况 然后成功挂掉。

    先从T3 说起。

    这道题 比较有意思 我觉得可以写但是实际上我没有思考周全 有点蒙蔽了早上 突然傻了。(可能昨天晚上3点睡的缘故。/cy

    首先就考虑爆搜 然后发现这个最多的gcd个数其实是包含最多质因子个数的 因为每次我都可以减小一个gcd来保证gcd的不同。

    那么此时 我的漏洞就是我质因数最多的那个数且<=n 其必然是2的若干次幂 因为如果是3或者是其他的话可以直接利用2来替换而且还可以更小。

    但是我忽略了一点 还有一个数字也可能是答案 2^(y-1)*3 这个数字包含的质因子个数和2^y的个数相同当同时<=n时都可以放到队首。

    我们讨论一下如何得到方案这个我想了好久好久 我没想到dp 直接算组合数了复杂度logn 是正解的复杂度 但是 少讨论了一种情况然后歇菜了。

    先考虑dp吧 f[i][j][k] 表示 前i个数字此时gcd是2^j*3^k的方案数 每一次要么不变要么少一个2 要么少一个3 随便转移一下即可。

     //#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
    ",f[n][0][0]);
        return 0;
    }
    View Code

    80分到手我这脑子就是sb了。

    优化也很简单 考虑组合意义 因为我们每一次不变的话就会带来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
    ",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
    ",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
    ",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
    ",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个重心怎么做?

  • 相关阅读:
    mysql自定义函数多表更新:update_order_relation()
    mysql自定义函数初始化数据:init_data()
    关于tomcat启动错误:At least one JAR was scanned for TLDs yet contained no TLDs
    intellij debug模式提示 Method breakpoints may dramatically slow down debugging
    Linux开机启动流程详细步骤是什么?
    chkconfig 添加脚本开机自启动
    优化SSH配置(一键完成增加若干参数)
    linux目录整理
    linux命令整理
    buffer和cache有什么区别?
  • 原文地址:https://www.cnblogs.com/chdy/p/11782656.html
Copyright © 2011-2022 走看看