来自学长的馈赠2

A. 随

开桶,然后就是乘

正解用什么多项式的思路考虑,但其实是个快速幂优化

发现开桶后就是一个特定乘法,满足结合率交换律,所以直接快速幂优化

code
#include<cstdio>
#include<cstring>
using namespace std;
const int MOD = 1e9 + 7;
const int maxn = 100005;
int n, m, mod;
int a[maxn],cnt[maxn],now[maxn],ls[maxn];
int qpow(int x,int y){
    int ans = 1;
    for(; y; y >>= 1, x = 1ll * x * x % MOD)if(y & 1)ans = 1ll * ans * x % MOD;
    return ans;
}
void mul(int x[],int y[]){
    for(int i = 1; i < mod; ++i)ls[i] = 0;
    for(int i = 1; i < mod; ++i)
      if(x[i]) 
        for(int j = 1; j < mod; ++j)
          if(y[j])ls[i * j % mod] = (ls[i * j % mod] + 1ll * x[i] * y[j] % MOD) % MOD;
    for(int i = 1; i < mod; ++i)x[i] = ls[i];
}
void qpow(int y){
    now[1] = 1;
    for(; y; y >>= 1){
        if(y & 1)mul(now, cnt);
        mul(cnt, cnt);
    }
}
int main(){
    scanf("%d%d%d",&n, &m, &mod);
    for(int i = 1; i <= n; ++i)scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i)++cnt[a[i]];
    qpow(m);
    int ans = 0;
    for(int i = 1; i < mod; ++i)ans = (ans + 1ll * now[i] * i % MOD) % MOD;
    ans = 1ll * ans * qpow(qpow(n, m), MOD - 2) % MOD;
    printf("%d\n",ans);
    return 0;
}

B. 单

原题, https://www.cnblogs.com/Chencgy/p/16313022.html

又打了一遍

code
#include<cstdio>
#include<cstring>

using namespace std;

const int maxn = 1e5 + 5;
typedef long long  ll;
int n, head[maxn], tot;
struct edge{
    int to,net;
}e[maxn << 1 | 1];
void add(int u, int v){
    e[++tot].net = head[u];
    head[u] = tot;
    e[tot].to = v;
}
ll rd[maxn], ot[maxn], dep[maxn], fa[maxn];
ll s[maxn];
void dfs_1(int x){
    for(int i = head[x]; i; i = e[i].net){
        int v = e[i].to;
        if(v == fa[x])continue;
        fa[v] = x;
        dfs_1(v);
    }
}
void ga(int x){
    ot[x] = s[x];
    for(int i = head[x]; i; i = e[i].net){
        int v = e[i].to;
        if(v == fa[x])continue;
        ot[x] -= s[v];
        ga(v);
    }
}
void work_a(){
    dfs_1(1);
    ll sum = 0;
    for(int i = 2; i <= n; ++i)sum += rd[i] - rd[fa[i]];
    sum += rd[1];sum += rd[1];
    s[1] = sum / (n - 1);
    for(int i = 2; i <= n; ++i)s[i] = (s[1] + rd[fa[i]] - rd[i]) / 2;
    ga(1);
}
void dfs(int x, int fa){
    s[x] = rd[x];
    for(int i = head[x]; i; i = e[i].net){
        int v = e[i].to;
        if(v == fa)continue;
        dep[v] = dep[x] + 1;
        dfs(v, x);
        s[x] += s[v];
    }
}
void dp(int x,int fa){
    for(int i = head[x]; i; i = e[i].net){
        int v = e[i].to;
        if(v == fa)continue;
        ot[v] = ot[x] - s[v] - s[v] + s[1];
        dp(v, x);
    }
}
void work_b(){
    dfs(1, 1);
    ot[1] = 0;
    for(int i = 1; i <= n; ++i)ot[1] += dep[i] * rd[i];
    dp(1, 1);
}
int main(){
    int T;scanf("%d", &T);
    for(int ask = 1; ask <= T; ++ask){
        scanf("%d",&n);
        for(int i = 1; i <= n; ++i)head[i] = 0;
        tot = 0;
        for(int i = 1; i < n; ++i){
            int u, v; scanf("%d%d", &u,&v);
            add(u,v);add(v,u);
        }
        int tp;scanf("%d",&tp);
        for(int i = 1; i <= n; ++i)scanf("%lld",&rd[i]);
        if(tp)work_a();
        else work_b();
        for(int i = 1; i <= n; ++i)printf("%lld ",ot[i]);
        printf("\n");
    }
    return 0;
}

C. 题

type

Cnn/2Cnn/2

选向上向右,选向上向左,交集为上,各自的为左/右,并集的补集为下

卡特兰数

dp,枚举ij歩走与之前不同的轴

初始f[0]=2因为开始可能走横轴也可能走纵轴

枚举横向步数和纵向步数

然后两个卡特兰乘起来即可

code
#include<cstdio>
#include<cstring>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 100000 + 5;
int qpow(int x,int y){
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
	return ans;
}
int fac[maxn], inv[maxn];
void pre(int n){
	fac[0] = 1;
	for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	inv[n] = qpow(fac[n], mod - 2);
	for(int i = n - 1; i; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
	inv[0] = 1;
}
int c(int n, int m){return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
int work_0(int n){
	return 1ll * c(n, n / 2) * c(n, n / 2) % mod;
}
int work_1(int n){
	return (0ll + c(n, n / 2) - c(n, n / 2 - 1) + mod) % mod;
}
int f[maxn];
int work_2(int n){
	f[0] = 2;
	for(int i = 2; i <= n; i += 2)
	  for(int j = 0; j < i; j += 2)
	    f[i] = (f[i] + 1ll * c(i - j, (i - j) / 2) * f[j] % mod) % mod;
	return f[n];
}
int work_3(int n){
	int ans = 0;
	for(int i = 0; i <= n; i += 2)ans = (ans + 1ll * c(n, i) * work_1(i) % mod * work_1(n - i) % mod) % mod;
	return ans;
}
int main(){
	int n, tp;
	scanf("%d%d",&n, &tp);
	pre(n);
	switch(tp){
		case 0: printf("%d\n",work_0(n));break;
		case 1: printf("%d\n",work_1(n));break;
		case 2: printf("%d\n",work_2(n));break;
		case 3: printf("%d\n",work_3(n));break;
	}    
	return 0;
}

D. DP搬运工1

这个题告诉我一个好的状态设计是多么重要

f[i][j][k]表示前i个数,有j个连续段,相邻max和为k

转移显然

考场状态设计有问题,n5复杂度爆炸而且还错了。。。

code
#include<cstdio>
#include<cstring>
using namespace std;
typedef  long long ll;
const int mod = 998244353;
const int maxn = 51;
int n, K;
int f[maxn][maxn][maxn * maxn];
int main(){
    scanf("%d%d",&n, &K);
    f[1][1][0] = 1;
    for(int i = 1 ; i < n; ++i){
        for(int j = 1; j <= i; ++j){
            for(int k = 0 ; k <= K; ++k){
                if(f[i][j][k]){
                    f[i + 1][j + 1][k] = (f[i + 1][j + 1][k] + f[i][j][k]) % mod;
                    f[i + 1][j][k + i + 1] = (f[i + 1][j][k + i + 1] + 1ll * 2 * j * f[i][j][k] % mod) % mod;
                    f[i + 1][j - 1][k + i + 1 + i + 1] = (f[i + 1][j - 1][k + i + 1 + i + 1] + 1ll * j * (j - 1) * f[i][j][k] % mod) % mod;
                }
            }
        }
    }
    ll ans = 0;
    for(int k = 0; k <= K; ++k) ans += f[n][1][k];
    printf("%lld\n",ans % mod);
    return 0;
}
posted @   Chen_jr  阅读(147)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示