今天不太行 刚了很久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; }
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; }
值得一提的是当 有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; }
但相比阶乘预处理的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个重心怎么作?