hs的太牛逼了……
搞成现在这德行我也好意思写blogTAT
T1 recompile
题目大意:(背景太高大上了……)
release(x):将节点x到root染成新的颜色
recenter(x):换root
query(x):查询x的子树的到根的平均颜色数
(先坑 有时间写 LCT 今天刚过模板题TAT
T2 一个n的全排列,要求不存在大小为1的环,若存在大小为k的环,则对ans[k]贡献为1,求sigma ans[i] (n<=500000)
考场上当然没想出来,出考场听说了错排……
然后听ac的讲了讲并没有听懂……
和妹子和学弟研究了一下,yy出了一个……也差不多吧……
首先,一个大小为k的环的方案数为k!/k=(k-1)!
对于n
g[i,j]表示大小为i的环至少出现j个的方案数
f[i]表示i的错排
g[i,1]=C(n,i)*(i-1)!*f[n-i]
g[i,2]=C(n,i)*(i-1)!*f[n-i]*C(n-i,i)*(i-1)!*f[n-2*i]
……
如果在规定时间内求出来,那么根据容斥原理 ans[i]=g[i,1]-g[i,2]+g[i,3]……
然后考虑时间复杂度,类似筛法求素数,均摊O(nlogn)
错排,阶乘,逆元都可以O(n)预处理
#include<cstdio>
#include<cstring>
#define N 500005
#define Mo (int)(1e9+7)
#define ll long long
using namespace std;
int n;
ll f[N],fac[N],inv[N];
ll ans;
ll pow(ll a,ll b,ll p){
ll base=a,res=1;
while(b){
if(b&1) res=res*base%p;
base=base*base%p;
b=b>>1;
}
return res;
}
ll C(int n,int m){
ll tmp=fac[n]*inv[m]%Mo;
tmp=tmp*inv[n-m]%Mo;
return tmp;
}
void init(){
scanf("%d\n",&n);
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%Mo;
inv[n]=pow(fac[n],Mo-2,Mo);
inv[0]=1;
for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%Mo;
f[0]=1;f[1]=0;f[2]=1;
for(int i=3;i<=n;i++) f[i]=(i-1)*(f[i-1]+f[i-2])%Mo;
}
void solve(){
ans=0;
ll last;
for(int i=2;i<=n;i++){
last=1;
for(int j=1;j<=(int)n/i;j++){
ll tmp=C(n-(j-1)*i,i)*fac[i-1]%Mo;
if(j&1) ans+=f[n-j*i]*tmp%Mo*inv[j]%Mo*last%Mo;
else ans-=f[n-j*i]*tmp%Mo*inv[j]%Mo*last%Mo;
ans=ans%Mo;
last=last*tmp%Mo;
}
}
printf("%lld\n",ans);
}
int main(){
freopen("cirperm.in","r",stdin);
freopen("cirperm.out","w",stdout);
init();
//for(int i=1;i<=n;i++) printf("%lld\n",f[i]);
solve();
return 0;
}
C.题目描述从一个全排列转成了一个逆序图,即一个逆序对连边。然后求这个图的既是独立集有事覆盖集的集合的个数。(n<=1000)
去年在八十做过
当时没做出来 但题解还是看了哒
然后这次就一眼喽……
转成序列,就是O(m)扫一遍,然后求极大上升子序列个数。n^2 dp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int Mo=(int)1e9+7;
const int N=1005;
const int M=1000005;
int left_smaller[M],right_smaller[M],a[N],dp[N];
int n,m,Max,ans;
int main(){
freopen("senritsu.in","r",stdin);
freopen("senritsu.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) left_smaller[i]=i-1;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
if(x>y) swap(x,y);
left_smaller[y]--;
right_smaller[x]++;
}
for(int i=1;i<=n;i++) a[i]=left_smaller[i]+right_smaller[i]+1;
//for(int i=1;i<=n;i++) printf("%d ",a[i]);
for(int i=1;i<=n;i++){
Max=0;
for(int j=i-1;j>=1;j--)
if(a[j]>Max && a[i]>a[j]){
dp[i]+=dp[j];
dp[i]%=Mo;
Max=a[j];
}
if(dp[i]==0) dp[i]=1;
}
//for(int i=1;i<=n;i++) printf("%d ",dp[i]);
Max=0;ans=0;
for(int i=n;i>=1;i--)
if(a[i]>Max){
ans+=dp[i];
ans%=Mo;
Max=a[i];
}
printf("%d\n",ans);
return 0;
}