hello 2019 (CF contest 1097)

https://codeforces.com/contest/1097

D. Makoto and a Blackboard

设$f(n)$为$n$的答案,那么由期望的定义可知$f$是个积性函数,那么问题转化成了求$f(p^x)$,其中$p$是质数。

那么直接暴力$dp$就好了,复杂度$O(\sqrt{n}+k\log^2 n)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<bits/stdc++.h>
using namespace std;

#define int long long

void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

#define lf double
#define ll long long

#define pii pair<int,int >
#define vec vector<int >

#define pb push_back
#define mp make_pair
#define fr first
#define sc second

#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)

const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int n,k,f[60][10030],inv[maxn];

int qpow(int a,int x) {
int res=1;
for(;x;x>>=1,a=1ll*a*a%mod) if(x&1) res=1ll*res*a%mod;
return res;
}

int solve(int x,int c) {
for(int i=0;i<=c+1;i++) inv[i]=qpow(i,mod-2);
memset(f,0,sizeof f);f[c][0]=1;
for(int i=1;i<=k;i++)
for(int j=0;j<=c;j++)
for(int t=j;t<=c;t++)
f[j][i]=(f[j][i]+1ll*inv[t+1]*f[t][i-1]%mod)%mod;
int res=0;
for(int i=0;i<=c;i++) res=(res+1ll*qpow(x,i)*f[i][k]%mod)%mod;
return res;
}

signed main() {
read(n),read(k);int ans=1;
for(int i=2;i*i<=n;i++) {
if(n%i) continue;
int c=0;while(n%i==0) n/=i,c++;
ans=1ll*ans*solve(i,c)%mod;
}
if(n!=1) ans=1ll*ans*solve(n,1)%mod;write(ans);
return 0;
}

E. Egor and an RPG game

神仙题啊啊啊。。

首先可以构造出最坏的情况就是类似于:$1,3,2,6,5,4,10,9,8,7$,显然这组的答案是$4$,也就是说对于一个$k$,最大长度为$k(k+1)/2$的排列最坏为$k$。

那么我们就可以算出$f(n)$了。

然后我们考虑如何构造方案,我们先求出$lis$,也就是最长上升子序列,假设$|lis|\geqslant k$,那么我们直接把$lis$拉出来然后变成一个子问题,因为$\frac{k(k+1)}{2}-k=\frac{k(k-1)}2$,所以剩下的子问题也合法。

否则我们可以直接选出$|lis|$个下降子序列,这里考虑$lis$的二分求法,设$f_i$表示长度为$i$的上升子序列结尾最小的数是多少。

那么每次添加一个新的数$x$我们就在$f$上二分然后更新,那么每个$f$的历史值一定递减并且位置递增。

总复杂度$O(n\sqrt n \log n)$,因为最坏要做$\sqrt n$次$lis$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

#define lf double
#define ll long long

#define pii pair<int,int >
#define vec vector<int >

#define pb push_back
#define mp make_pair
#define fr first
#define sc second

#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)

const int maxn = 1e6+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int a[maxn],n,t[maxn],len,sta[maxn],top,pre[maxn],rev[maxn];
vector<int > s[maxn];

void solve() {
read(n);for(int i=1;i<=n;i++) read(a[i]),rev[a[i]]=i;
vector<vector<int > > ans;
while(n) {
for(int i=0;i<=n;i++) t[i]=0,s[i].clear(),pre[i]=0;len=0;
vector<int > r;
for(int i=1;i<=n;i++) {
if(a[i]>t[len]) {
t[++len]=a[i],s[len].pb(a[i]);
pre[i]=rev[t[len-1]];
continue;
}
int x=lower_bound(t+1,t+len+1,a[i])-t;
s[x].pb(a[i]),t[x]=a[i];pre[i]=rev[t[x-1]];
}
int k=0;while((k+1)*(k+2)/2<=n) k++;
if(len<=k) {
for(int i=1;i<=len;i++) ans.pb(s[i]);
break;
}for(int i=rev[t[len]];i;i=pre[i]) r.pb(a[i]);
reverse(r.begin(),r.end());ans.pb(r);
int p=0;top=0;
for(int i=1;i<=n;i++) if(p>=r.size()||a[i]!=r[p]) sta[++top]=a[i];else p++;
for(int i=1;i<=top;i++) a[i]=sta[i],rev[a[i]]=i;
n-=len;
}write(ans.size());
for(int i=0;i<ans.size();i++) {
printf("%d ",ans[i].size());
for(int j=0;j<ans[i].size();j++) printf("%d ",ans[i][j]);puts("");
}
}

int main() {
int t;read(t);while(t--) solve();
return 0;
}

F. Alex and a TV Show

如果没有$3$操作那么显然可以用$\rm bitset$。

否则我们考虑一种这样的变换,原来假设$f_{i,j}$表示$i$集合中$j$的数量,现在我们改成$f_{i,j}$表示$i$集合中$j$的倍数的数量,那么$3$操作直接逐位相乘就行了。

$1$操作显然可以$O(v^2)$预处理,$3$操作直接利用莫比乌斯反演,也可以$O(v^2)$预处理系数然后直接转化为$\rm bitset$运算,$2$操作和原来没有区别。

那么总复杂度就是$O(\frac{qv}{\omega}+v^2)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

#define lf double
#define ll long long

#define pii pair<int,int >
#define vec vector<int >

#define pb push_back
#define mp make_pair
#define fr first
#define sc second

#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)

const int maxn = 1e5+10;
const int M = 7001;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int n,q,pri[maxn],mu[maxn],vis[maxn],tot;
bitset<M > t[maxn],r[M],s[M];

void sieve() {
mu[1]=1;
for(int i=2;i<M;i++) {
if(!vis[i]) mu[i]=1,pri[++tot]=i;
for(int j=1;j<=tot&&i*pri[j]<M;j++) {
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
mu[i*pri[j]]=mu[i];
}
}
}

int main() {
read(n),read(q);sieve();
for(int i=1;i<M;i++)
for(int j=1;j<=i;j++) if(i%j==0) r[i][j]=1;
for(int i=1;i<M;i++)
for(int j=i;j<M;j+=i) s[i][j]=mu[j/i];
while(q--) {
int op,x,y,z,v;read(op),read(x);
if(op==1) read(v),t[x]=r[v];
else if(op==2) read(y),read(z),t[x]=t[y]^t[z];
else if(op==3) read(y),read(z),t[x]=t[y]&t[z];
else read(v),putchar(((t[x]&s[v]).count()&1)+'0');
}
return 0;
}

G. Vladislav and a Great Legend

什么鬼题。。。看到$k$只有$200$先用斯特林数搞一波,式子变成:

第一个式子是第二类斯特林数的套路,后面是下降幂,不会的可以看看这个:斯特林反演

然后我们现在就是要求后面那一块,设$f_{i,j}$表示$i$的子树里选中了$j$条边,对于每一个边的选法选点的方案数之和。

那么这个可以类似树上背包的转移,就是当前新加一个子树讨论下这个子树怎么选。

如果每次循环边界都写成$\min(k,sz_v)$这样,复杂度就是$O(nk)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<bits/stdc++.h>
using namespace std;

void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}

void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

#define lf double
#define ll long long

#define pii pair<int,int >
#define vec vector<int >

#define pb push_back
#define mp make_pair
#define fr first
#define sc second

#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)

const int maxn = 1e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;

int s[202][202],f[maxn][202],fac[maxn],n,k,head[maxn],tot,sz[maxn],res[maxn],t[maxn];
struct edge{int to,nxt;}e[maxn<<1];

void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}

void prepare() {
fac[0]=1;for(int i=1;i<maxn;i++) fac[i]=1ll*fac[i-1]*i%mod;
s[0][0]=1;
for(int i=1;i<=200;i++)
for(int j=1;j<=i;j++) s[i][j]=(1ll*j*s[i-1][j]%mod+s[i-1][j-1]%mod)%mod;
}

void dfs(int x,int fa) {
sz[x]=1;f[x][0]=1;
for(int pps=head[x],v;pps;pps=e[pps].nxt) {
if((v=e[pps].to)==fa) continue;dfs(v,x);
for(int i=0;i<=min(k,sz[x]);i++) t[i]=f[x][i];
for(int i=1;i<=min(k,sz[v]);i++) f[x][i]=(0ll+f[x][i]+f[v][i]+f[v][i-1])%mod;
f[x][0]=(f[x][0]+f[v][0])%mod;
for(int i=0;i<=min(k,sz[x]);i++)
for(int j=0;j<=min(k-i,sz[v]);j++) {
f[x][i+j]=(f[x][i+j]+1ll*t[i]*f[v][j]%mod)%mod;
f[x][i+j+1]=(f[x][i+j+1]+1ll*t[i]*f[v][j]%mod)%mod;
res[i+j]=(res[i+j]+1ll*t[i]*f[v][j]%mod)%mod;
res[i+j+1]=(res[i+j+1]+1ll*t[i]*f[v][j]%mod)%mod;
}
sz[x]+=sz[v];
}
}

int main() {
prepare();read(n),read(k);
for(int i=1,x,y;i<n;i++) read(x),read(y),ins(x,y),ins(y,x);
dfs(1,0);int ans=0;
for(int i=1;i<=k;i++) ans=(ans+1ll*s[k][i]*fac[i]%mod*res[i]%mod)%mod;
write(ans);
return 0;
}