牛客练习赛64题解

题目链接

A.怪盗-1412

题意:
有 n 个 1 , m 个 4 , k 个 2 有n个1,m个4,k个2 n1m4k2
组 成 一 个 序 列 问 最 多 能 有 多 少 子 序 列 是 1412 组成一个序列问最多能有多少子序列是1412 1412
题解:
摆 成 11 … 1122 … 2211 … 1144 … … 的 样 子 摆成11…1122…2211…1144……的样子 11112222111144
这 样 能 使 得 每 个 数 都 有 贡 献 这样能使得每个数都有贡献 使
根 据 和 一 样 , 两 数 差 最 小 乘 积 最 大 可 以 知 道 平 分 1 即 可 根据和一样,两数差最小乘积最大可以知道平分1即可 1
四 个 位 置 个 数 相 乘 四个位置个数相乘

#include
using namespace std;
int main(){
    int _;
    scanf("%d",&_);
    while(_--){
        long long n,m,k;
        scanf("%lld%lld%lld",&n,&m,&k);
        printf("%lld\n",(n/2)*m*k*(n-n/2));
    }
}

B.Dis2

题意:
给 一 棵 树 , 问 每 个 点 有 多 少 和 他 距 离 为 2 的 点 给一棵树,问每个点有多少和他距离为2的点 2
题解:
对 于 每 个 点 来 说 , 距 离 为 2 的 点 , 就 是 这 个 点 的 子 节 点 的 子 节 点 对于每个点来说,距离为2的点,就是这个点的子节点的子节点 2
统 计 一 下 这 个 点 相 连 的 点 , 每 个 点 的 度 数 统计一下这个点相连的点,每个点的度数
但 由 于 是 双 向 边 , 需 要 去 除 自 己 的 贡 献 但由于是双向边,需要去除自己的贡献
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll ans[maxn];
vector<int> g[maxn];


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;cin>>n;
    for(int i=1,u,v;i<n;i++){
        cin>>u>>v;
        g[u].pb(v);
        g[v].pb(u);
    }
    for(int u=1;u<=n;u++){
        ll ans=0;
        for(auto v:g[u]){
            if(v==u)continue;
            ans+=g[v].size()-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}


C.序列卷积之和

题意:
给 一 个 长 度 为 n 的 数 组 给一个长度为n的数组 n
求 ∑ l = 1 n ∑ r = l n ∑ i = l r ∑ j = i r a i ∗ a j   m o d   1 e 9 + 7 求\sum_{l=1}^n\sum_{r=l}^n\sum_{i=l}^r\sum_{j=i}^ra_i*a_j~mod~1e9+7 l=1nr=lni=lrj=iraiaj mod 1e9+7
题解:
n < = 2 e 5 , 很 大 n<=2e5,很大 n<=2e5
暴 力 求 解 就 不 用 想 了 , 考 虑 一 下 怎 么 样 化 简 暴力求解就不用想了,考虑一下怎么样化简
自 己 举 个 例 子 感 受 一 下 , 不 要 太 多 数 自己举个例子感受一下,不要太多数
我 这 里 举 4 个 数 , 1 , 2 , 3 , 4 我这里举4个数,1,2,3,4 41234
然 后 先 走 前 两 个 确 定 , 结 果 是 1 然后先走前两个确定,结果是1 1
然 后 第 一 个 不 变 , 第 二 个 累 加 区 间 加 然后第一个不变,第二个累加区间加
得 到 1 ∗ 1 + 1 ∗ 2 + 2 ∗ 2 得到1*1+1*2+2*2 11+12+22
再 变 一 次 1 ∗ 1 + 1 ∗ 2 + 1 ∗ 3 + 2 ∗ 2 + 2 ∗ 3 + 3 ∗ 3 再变一次1*1+1*2+1*3+2*2+2*3+3*3 11+12+13+22+23+33

1 ∗ 1 + 1 ∗ 2 + 1 ∗ 3 + 1 ∗ 4 + 2 ∗ 2 + 2 ∗ 3 + 2 ∗ 4 + 3 ∗ 3 + 3 ∗ 4 + 4 ∗ 4 1*1+1*2+1*3+1*4+2*2+2*3+2*4+3*3+3*4+4*4 11+12+13+14+22+23+24+33+34+44
这 样 第 一 个 累 加 为 1 的 时 候 跑 完 了 这样第一个累加为1的时候跑完了 1
我 们 可 以 化 简 一 下 我们可以化简一下
1 ∗ ( 1 + 1 + 2 + 1 + 2 + 3 + 1 + 2 + 3 + 4 ) 1*(1+1+2+1+2+3+1+2+3+4) 1(1+1+2+1+2+3+1+2+3+4)
2 ∗ ( 2 + 2 + 3 + 2 + 3 + 4 ) 2*(2+2+3+2+3+4) 2(2+2+3+2+3+4)
3 ∗ ( 3 + 3 + 4 ) 3*(3+3+4) 3(3+3+4)
4 ∗ 4 4*4 44
然 后 可 以 想 象 一 下 , 或 者 大 概 再 往 后 下 一 点 就 能 发 现 然后可以想象一下,或者大概再往后下一点就能发现
第 一 个 累 加 为 2 的 时 候 , 其 实 就 是 这 式 子 里 的 后 三 个 第一个累加为2的时候,其实就是这式子里的后三个 2
为 3 的 时 候 是 后 两 个 为3的时候是后两个 3
这 规 律 就 找 到 了 这规律就找到了
我 们 只 要 把 这 些 括 号 里 的 东 西 算 出 来 和 数 相 乘 , 最 后 求 一 下 后 缀 的 后 缀 就 是 答 案 了 我们只要把这些括号里的东西算出来和数相乘,最后求一下后缀的后缀就是答案了 西
括 号 里 的 可 以 用 个 数 算 , 我 们 可 以 发 现 第 i 个 数 一 共 有 n − i + 1 个 括号里的可以用个数算,我们可以发现第i个数一共有n-i+1个 ini+1
然 后 我 们 用 后 缀 跑 一 下 每 个 括 号 里 每 个 数 的 个 数 乘 对 应 的 值 然后我们用后缀跑一下每个括号里每个数的个数乘对应的值
这 样 括 号 里 的 东 西 算 出 来 了 , 假 设 是 s u m i 这样括号里的东西算出来了,假设是sum_i 西sumi
∑ i = 1 n a i ∗ s u m i 就 是 第 一 个 累 加 为 1 时 候 的 结 果 \sum_{i=1}^na_i*sum_i就是第一个累加为1时候的结果 i=1naisumi1
∑ i = 2 n a i ∗ s u m i 就 是 第 一 个 累 加 为 2 时 候 的 结 果 \sum_{i=2}^na_i*sum_i就是第一个累加为2时候的结果 i=2naisumi2
所 以 这 个 时 候 我 们 就 需 要 求 一 下 后 缀 的 后 缀 算 出 来 第 一 个 累 加 为 1 到 n 全 部 的 和 所以这个时候我们就需要求一下后缀的后缀算出来第一个累加为1到n全部的和 1n
但 其 实 有 更 简 便 的 方 法 , 我 们 直 接 记 个 数 但其实有更简便的方法,我们直接记个数 便
假 设 下 标 为 i 的 a i ∗ s u m i 我 们 用 的 次 数 应 该 是 i 次 假设下标为i的a_i*sum_i我们用的次数应该是i次 iaisumii
所 以 i 从 1 到 n 直 接 将 每 个 值 乘 他 的 贡 献 次 数 就 是 最 终 答 案 所以i从1到n直接将每个值乘他的贡献次数就是最终答案 i1n

如 果 你 感 觉 这 样 的 规 律 不 好 看 出 来 如果你感觉这样的规律不好看出来
那 你 可 以 考 虑 , 去 掉 两 层 内 部 的 累 加 , 用 代 码 输 出 一 下 找 一 下 规 律 那你可以考虑,去掉两层内部的累加,用代码输出一下找一下规律
这 个 规 律 应 该 也 很 好 看 出 , 其 实 就 是 我 在 上 面 说 的 规 律 这个规律应该也很好看出,其实就是我在上面说的规律
但 是 会 更 加 直 观 但是会更加直观
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
ll a[maxn],s1[maxn];


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=n;i;i--)
        s1[i]=(s1[i+1]+a[i]*(n-i+1)%mod)%mod;
    ll ans=0;
    for(int i=1;i<=n;i++)
        ans=(ans+i*a[i]%mod*s1[i]%mod)%mod;;
    cout<<ans;
    return 0;
}


D.宝石装箱

题意:
n 个 宝 石 放 进 n 个 盒 子 n个宝石放进n个盒子 nn
如 果 第 i 种 宝 石 不 能 放 到 a i 盒 子 有 多 少 种 方 案 如果第i种宝石不能放到a_i盒子有多少种方案 iai
m o d   998244353 mod~998244353 mod 998244353
题解:
如 果 是 直 接 写 , 我 们 需 要 考 虑 对 于 第 i 种 宝 石 如果是直接写,我们需要考虑对于第i种宝石 i
a i 盒 子 是 否 已 经 被 放 了 宝 石 , 最 后 得 出 的 会 是 两 种 情 况 a_i盒子是否已经被放了宝石,最后得出的会是两种情况 ai
我 们 很 难 统 计 这 两 种 情 况 我们很难统计这两种情况
那 就 倒 着 写 , 先 随 意 放 , 看 看 其 中 有 多 少 不 合 法 的 方 案 那就倒着写,先随意放,看看其中有多少不合法的方案
总 方 案 很 好 求 , 就 是 n ! 总方案很好求,就是n! n!
现 在 需 要 求 的 是 不 合 法 的 方 案 现在需要求的是不合法的方案
不 合 法 的 方 案 就 是 某 个 盒 子 放 了 不 该 放 的 宝 石 不合法的方案就是某个盒子放了不该放的宝石
我 们 需 要 统 计 最 后 有 用 的 就 是 i 个 盒 子 放 了 不 该 放 的 宝 石 的 方 案 我们需要统计最后有用的就是i个盒子放了不该放的宝石的方案 i
但 是 恰 好 i 个 我 们 很 难 求 出 , 我 们 可 以 用 至 少 有 i 个 盒 子 放 了 不 该 放 的 宝 石 但是恰好i个我们很难求出,我们可以用至少有i个盒子放了不该放的宝石 ii
如 果 是 至 少 i 个 盒 子 , 那 么 肯 定 有 重 复 的 方 案 如果是至少i个盒子,那么肯定有重复的方案 i
这 时 候 就 可 以 用 的 容 斥 原 理 了 , 奇 数 加 偶 数 减 , 容 斥 原 理 就 不 多 说 这时候就可以用的容斥原理了,奇数加偶数减,容斥原理就不多说
现 在 开 始 开 始 说 怎 么 求 至 少 i 个 盒 子 放 了 不 该 放 的 现在开始开始说怎么求至少i个盒子放了不该放的 i
其 实 就 是 统 计 出 哪 几 个 位 置 , 有 几 个 不 该 放 的 宝 石 其实就是统计出哪几个位置,有几个不该放的宝石
不 该 放 的 宝 石 个 数 , 就 是 这 一 个 点 的 方 案 数 不该放的宝石个数,就是这一个点的方案数
对 于 多 个 位 置 就 是 相 乘 起 来 对于多个位置就是相乘起来
由 于 统 计 的 是 需 要 将 所 有 宝 石 放 入 , 对 于 其 他 宝 石 也 需 要 进 行 放 入 由于统计的是需要将所有宝石放入,对于其他宝石也需要进行放入
那 么 就 是 乘 剩 余 没 有 选 择 的 个 数 的 阶 乘 , 我 们 已 经 选 择 的 是 确 定 不 合 法 的 那么就是乘剩余没有选择的个数的阶乘,我们已经选择的是确定不合法的
这 一 过 程 我 们 可 以 使 用 01 背 包 + 滚 动 数 组 实 现 这一过程我们可以使用01背包+滚动数组实现 使01+
状 态 转 移 很 简 单 , 就 是 d p [ j ] = d p [ j − 1 ] ∗ a [ i ] 状态转移很简单,就是dp[j]=dp[j-1]*a[i] dp[j]=dp[j1]a[i]
求 出 来 的 是 我 们 确 定 不 合 法 的 盒 子 数 求出来的是我们确定不合法的盒子数
然 后 剩 下 的 就 是 把 他 乘 上 对 应 的 阶 乘 形 成 完 整 方 案 , 然 后 进 行 容 斥 去 重 即 可 然后剩下的就是把他乘上对应的阶乘形成完整方案,然后进行容斥去重即可
最 后 用 总 数 减 去 这 个 算 出 来 的 数 最后用总数减去这个算出来的数
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll fac[maxn],dp[maxn],a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;cin>>n;fac[0]=1;
    for(int i=1,x;i<=n;i++){
        cin>>x;
        a[x]++;
        fac[i]=fac[i-1]*i%mod;
    }
    dp[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=i;j>=0;j--)
            dp[j+1]=(dp[j+1]+dp[j]*a[i]%mod)%mod;
    ll ans=fac[n],res=0;
    for(int i=1,p=1;i<=n;i++,p=-p)
        res=(res+fac[n-i]*dp[i]*p%mod+mod)%mod;
    cout<<(ans-res+mod)%mod;
    return 0;
}

你可能感兴趣的:(牛客练习赛64题解)