来自学长的馈赠4
打了一半就跑了,,,垫底垫底
A. 活动投票
摩尔投票,想象让投票不同的两个人同归于尽,选票过半的人的支持者肯定有活下来的
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
int n;
int main(){
n = read();
int ans = 0, cnt = 0;
for(int i = 1; i <= n; ++i){
int x = read();
if(cnt && x != ans)--cnt;
else ++cnt, ans = x;
}
printf("%d\n",ans);
return 0;
}
B. 大佬
每 天之间相互独立?
计算天的期望乘上,再除以总方案数即可
期望计算方法就是枚举最大值简单容斥
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
const int maxn = 5005;
const int mod = 1000000007;
int n, m, k, wt[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;
}
int main(){
n = read(); m = read(); k = read();
for(int i = 1; i <= m; ++i)wt[i] = read();
int ans = 0, las = 0;
for(int i = 1; i <= m; ++i){
int ls = qpow(i, k);
ans = (ans + 1ll * (ls - las + mod) % mod * wt[i] % mod) % mod;
las = ls;
}
ans = 1ll * qpow(qpow(m, k), mod - 2) * ans % mod * max(n - k + 1, 0) % mod;
printf("%d\n",ans);
return 0;
}
C. Dp搬运工3
如果你和我一样不会摩尔投票,那么这就是全场最水的题
表示前个数,个数没有配对,当前答案为的方案数
转移考虑当前两个数是否配对,跟谁配对
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 53;
const int mod = 998244353;
ll f[maxn][maxn][maxn * maxn];
int main(){
ll n,K; scanf("%lld%lld",&n,&K);
f[1][1][0] = n * (n - 1);
f[1][0][1] = n;
for(ll i = 1; i < n; ++i){
ll mj = min(i, n - i);
for(ll j = 0; j <= mj; ++j){
for(ll k = 0; k <= K; ++k)
if(f[i][j][k]){
f[i][j][k] %= mod;
f[i + 1][j][k + i + 1] += f[i][j][k] * (n - i - j);
f[i + 1][j + 1][k] += f[i][j][k] * (n - i - j) * (n - i - j - 1);
f[i + 1][j][k + i + 1] += f[i][j][k] * 2 * (n - i - j) * j;
if(j)f[i + 1][j - 1][k + i + 1 + i + 1] += f[i][j][k] * j * j;
}
}
}
ll ans = 0;
for(ll i = 0; i < K; ++i)ans += f[n][0][i];
ans %= mod;
ll sum = 1;
for(ll i = 1; i <= n; ++i)sum = sum * i % mod;
sum = sum * sum % mod;
printf("%lld\n",((sum - ans) % mod + mod) % mod);
return 0;
}
D. Beautiful
康拓展开 + 错排应用 + 简单? + 树状数组
第一行康拓展开
后面比较麻烦,假设你考虑到了位置,那么有些数字有错排限制,有些没有,这个需要预处理出来
然后我需要知道有多少有限制的数小于当前值,多少没限制的数小于当前值,维护两个树状数组即可
没懂 ? 看代码吧。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x;
}
const int maxn = 2005;
const int mod = 998244353;
int n, a[maxn][maxn], cp[maxn], bcp[maxn][maxn], fac[maxn];
bool vis1[maxn], vis2[maxn];
struct tree{
int t[maxn];
int lowbit(int x){return x & -x;}
void add(int x, int d){while(x <= n){t[x] += d; x += lowbit(x);}}
int query(int x){int ans = 0; while(x){ans += t[x]; x -= lowbit(x);}return ans;}
}T1,T2;
inline 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 main(){
n = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
a[i][j] = read();
cp[2] = 1; for(int i = 3; i <= n; ++i)cp[i] = 1ll * (i - 1) * (cp[i - 1] + cp[i - 2]) % mod;
fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
bcp[0][0] = 1;// bcp[i][j]i个数,其中j 个有错排限制
for(int i = 1; i <= n; ++i){
bcp[i][0] = fac[i];//钦定一个无限制的位置,考虑放一个有/无限制的数
for(int j = 1; j < i; ++j)bcp[i][j] = (1ll * j * bcp[i - 1][j - 1] + 1ll * bcp[i - 1][j] * (i - j)) % mod;
bcp[i][i] = cp[i];
}
int ktzk = 0;
for(int i = 1; i <= n; ++i)T1.add(i, 1);
for(int i = 1; i <= n; ++i){
ktzk = (ktzk + 1ll * T1.query(a[1][i] - 1) * fac[n - i] % mod) % mod;
T1.add(a[1][i], -1);
}
int ans = 1ll * qpow(cp[n], n - 1) * ktzk % mod;
for(int i = 2; i <= n; ++i){
for(int j = 1; j <= n; ++j)T1.t[i] = 0;
for(int j = 1; j <= n; ++j)vis1[j] = 0;
for(int j = 1; j <= n; ++j)vis2[j] = 0;
for(int j = 1; j <= n; ++j)T2.add(j, 1);
//T1维护往后没有错排限制的数,T2维护有错排限制的数
//vis1是上一行的vis,vis2是本行的vis
int ls = 0, res = n;
for(int j = 1; j <= n; ++j){
if(!vis2[a[i - 1][j]])--res, T2.add(a[i - 1][j], -1);//本行没有出现过a[i - 1][j],那么它不再是有限制的数,但是它不能出现在当前位置。所以加入操作在统计答案之后
ls = (ls + 1ll * T1.query(a[i][j] - 1) * bcp[n - j][res] % mod) % mod;//放无限制,比当前小
if(res) ls = (ls + 1ll * T2.query(a[i][j] - 1) * bcp[n - j][res - 1] % mod) % mod;//有限制,比当前小
if(!vis2[a[i - 1][j]])T1.add(a[i - 1][j], 1);//加入操作
if(vis1[a[i][j]])T1.add(a[i][j], -1);//如果上一行之前访问过a[i][j]那么此时它没有限制,从T1中删
else T2.add(a[i][j], -1);//否则它在T2中删
//这里是在消除a[i][j],因为之后不能用它了
vis1[a[i - 1][j]] = vis2[a[i][j]] = 1;//打vis
if(!vis1[a[i][j]])--res;//上一行没有出现过当前数,它没有限制了 res = T2.query(n)
}
ans = (ans + 1ll * ls * qpow(cp[n],n - i) % mod) % mod;
}
printf("%d\n",ans % mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】