「解题报告」2023-10-18 模拟赛
同色三角形#
题目描述#
有一个 个点的完全图,点分别被编号为 ,每个点都有 条边连向其它点。每条边有绿色或者红色两种颜色,现在我们知道每个点连着几条绿色边,几条红色边,但不知道每条边具体连接哪个点。
在完全图中任选三个点,观察它们之间的三条边,如果三条边颜色相同,那么我们就称其为“同色三角形”。现在牛牛已经知道了每个点连着几条绿色、红色边(保证这样的图一定存在),他想要知道图中最多有几个同色三角形。
很可惜,牛牛是色盲,所以这个问题只能交给你了。
输入描述:#
第一行输入一个正整数 ,表示共有 个点。
接下来包含 行,每行给两个数字 ,其中第 行的数字表示第 个点的红色边、绿色边数量,保证 。
输出描述:#
输出一行一个整数表示答案。
示例1
输入#
4
1 2
2 1
2 1
3 0
输出#
1
说明#
备注:#
对于 的数据,有
对于 的数据,有
对于 的数据,有
对于 的数据,有
对于 的数据,有
对于 的数据,有
容斥原理,用总的方案减去异色的方案。
总的方案:,异色方案:。
// The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
template<typename T>
void write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
template<typename T>
void print(T x, char c) {
write(x);
putchar(c);
}
const int N = 3e5 + 5;
ll n;
int a[N], b[N];
int main() {
n = read<int>();
ll ans = 0;
rep (i, 1, n, 1) {
a[i] = read<int>(), b[i] = read<int>();
ans = (ans + 1ll * a[i] * b[i]);
}
ans /= 2;
ll sum = 1;
sum = n * (n - 1) * (n - 2);
sum /= 6;
cout << sum - ans << '\n';
return 0;
}
任务#
白浅妹妹有 个任务和一个长度为 的数组 ,数组中的第 个数字记为 。
白浅妹妹的第 个任务是用一个字符串 描述的,意思是需要构造一个长度为 的字符串 ,使得 是 的子序列。
现在给定 次操作,每次给出三个正整数。其中第一个正整数为 或 ,表示操作类型。
操作类型 :给定 ,表示将 修改为 。
操作类型 :给定 ,表示询问白浅妹妹从第 个任务一直完成到第 个任务,她总共有多少种方案构造字符串。
请注意,在白浅妹妹完成 这些字符串的过程的若干方案中,只要有任何一个任务构造的字符串不同,我们则认为这是不同的方案。
由于答案可能很大,请输出答案对 取模之后的结果。
输入描述:#
第一行输入两个正整数 。
接下来 行,分别表示所有的
接下来一行给出 个由空格隔开的正整数,表示数组 ,
接下来 行,每行给出三个正整数,意义如题面所示。三个正整数中的第一个正整数为 或 ,表示两种操作。
输出描述:#
对于每个操作 ,输出一行一个正整数表示答案对 取模之后的结果。
示例1
输入#
3 4
ab
b
c
3 2 1
1 1 1
1 2 2
1 1 2
1 3 3
输出#
76
51
3876
1
示例2
输入#
2 3
a
bc
2 6
1 1 2
0 2 3
1 2 2
输出#
315251451
76
备注:#
对于 的数据,有
对于 的数据,有
对于 的数据,有
对于 的数据,有
我们考虑如何求出包含 这个子序列的长为 的字符串 有多少个。令 表示目前枚举到了 的第 位,匹配到了 的第 位的方案数。那么
(要么第 位匹配上了,那么 的取值就只有一种,要么第 位没有匹配上,那么取值就有 个)
所以我们发现答案只与 有关,与 内容无关。如果处理好这些 的话,将可以获得 的暴力分。
100pt
因为 ,所以 至多有 种。那么我们就可以维护一个单点修改,区间查询乘积的线段树了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const ll Maxn=1e5,Maxs=3e5,mod=998244353,Maxf=8e2;
ll n,q,a[Maxn+10];
int dp[Maxf+10][Maxn+10];
char s[Maxs+10];
bool cmp(ll x,ll y){return x<y;}
ll maxll(ll x,ll y){return x>y?x:y;}
ll minll(ll x,ll y){return x<y?x:y;}
ll tub[Maxs+10],pos[Maxn+10],lenf,len[Maxf+10];
ll tb_25[Maxn+10],jc[Maxn+10];
ll inv[Maxn+10],inv_jc[Maxn+10];
vector<ll>id[Maxn+10];
ll C(ll x,ll y){//x个中选y个
return ((jc[x]*inv_jc[y]%mod)*inv_jc[x-y])%mod;
}
ll al,ar;
ll tr[5*Maxn+10];
void build(ll l,ll r,ll p){
if(l==r){
tr[p]=dp[pos[l]][a[l]];
return ;
}
ll mid=(l+r)>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
tr[p]=tr[p<<1]*tr[p<<1|1]%mod;
}
ll query(ll l,ll r,ll p){
if(r<al||ar<l)
return 1ll;
if(al<=l&&r<=ar)
return tr[p];
ll ret=1,mid=(l+r)>>1;
if(mid>=al)
ret=ret*query(l,mid,p<<1)%mod;
if(mid+1<=ar)
ret=ret*query(mid+1,r,p<<1|1)%mod;
return ret%mod;
}
void update(ll l,ll r,ll p){
if(r<al||ar<l)
return ;
if(l==al&&r==ar&&l==r){
tr[p]=dp[pos[l]][a[l]];
return ;
}
ll mid=(l+r)>>1;
if(mid>=al)
update(l,mid,p<<1);
if(mid+1<=ar)
update(mid+1,r,p<<1|1);
tr[p]=tr[p<<1]*tr[p<<1|1]%mod;
}
int main(){
tb_25[0]=1;
for(ll i=1;i<=Maxn;i++)
tb_25[i]=(tb_25[i-1]*25ll)%mod;
jc[1]=inv[1]=inv_jc[1]=1;
jc[0]=1,inv_jc[0]=1,inv[0]=1;
for(ll i=2;i<=Maxn;i++){
jc[i]=(jc[i-1]*i)%mod;
inv[i]=((mod-mod/i)*inv[mod%i])%mod;
inv_jc[i]=inv[i]*inv_jc[i-1]%mod;
}
scanf("%lld%lld",&n,&q);
ll slen=0;
for(ll i=1;i<=n;i++){
scanf("%s",s+1);
slen=strlen(s+1);
tub[slen]++;
id[slen].push_back(i);
}
for(ll i=1;i<=Maxs;i++){
if(tub[i]==0)
continue;
lenf++;
len[lenf]=i;
for(ll i1=0;i1<tub[i];i1++)
pos[id[i][i1]]=lenf;
}
for(ll i=1;i<=lenf;i++)
for(ll i1=len[i];i1<=Maxn;i1++)
dp[i][i1]=(int)((C(i1-1,len[i]-1)*tb_25[i1-len[i]])%mod+dp[i][i1-1]*26ll%mod)%mod;
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
ll l=0,r=0,cs=0,sum=0;
build(1,n,1);
for(ll i=1;i<=q;i++){
sum=1;
scanf("%lld%lld%lld",&cs,&l,&r);
if(cs==1){
al=l,ar=r;
printf("%lld\n",query(1,n,1));
}
else{
al=ar=l,a[l]=r;
update(1,n,1);
}
}
return 0;
}
加法方案#
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述#
白浅妹妹正在学习加法,但是老师只给了她一个数字 ,她没法对一个数字做加法运算,于是她从 中取出若干个数位(至少 个),然后按照原来的相对顺序拼接组成新一个数字 ,剩余的数位也按原来的相对顺序组成另一个数字 ,将两个数字 求和。
例如 可以拿出 ,剩下 ,求和 。
求所有拆数字的方案的和对 取模后的结果。
允许把 中所有的数位全部取出,此时 变成 。
记 。 从高到低位记为 到 ,即。记 是 的子集。每个 对应特定的 的子序列,即 ,则 。两个方案不同等价于 不同。
输入描述:#
输入包含一行。
第一行输入一个正整数 。
输出描述:#
输出一个数,即所有拆数字的方案的和对 取模后的结果。
示例1
输入#
123
输出#
231
说明#
取 个数位:
取 个数位:
取 个数位:
全部求和得到
示例2
输入#
221
输出#
359
备注:#
-
对于测试点 :。
-
对于测试点 :。
-
对于测试点 :。
60 分暴力:#
可以发现答案即为这个式子:
// The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
template<typename T>
void write(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) {
write(x / 10);
}
putchar(x % 10 + '0');
}
template<typename T>
void print(T x, char c) {
write(x);
putchar(c);
}
const int N = 1e5 + 5;
const int mod = 998244353;
ll ans;
char s[N];
int a[N];
ll fac[N], inv[N];
ll qpow(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1) {
res = res * x % mod;
}
y >>= 1;
x = x * x % mod;
}
return res % mod;
}
ll C(int n, int m) {
if (n < m) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int main() {
scanf("%s", s);
int len = strlen(s);
rep (i, 1, len, 1) {
a[i] = s[i - 1] - '0';
}
fac[0] = 1;
rep (i, 1, len, 1) {
fac[i] = fac[i - 1] * i % mod;
}
inv[len] = qpow(fac[len], mod - 2);
per (i, len, 1, 1) {
inv[i - 1] = inv[i] * i % mod;
}
rep (i, 1, len, 1) {
ll res = 0;
rep (j, 0, len - i, 1) {
res = (res + C(len - i, j) * qpow(10, j) % mod) % mod;
}
res = (res * qpow(2, i) % mod * a[i]) % mod;
ans = (ans + res) % mod;
}
rep (i, 1, len, 1) {
ans = (ans - a[i] * qpow(10, len - i) % mod + mod) % mod;
}
print(ans, '\n');
return 0;
}
发现若 可以为空,则 可以和 构成双射,则最终答案即为 。
考虑求 ,可以考虑每一位的贡献,则有从高到低第 位的贡献为
考虑这个 式子的组合意义,记 ,即为给出标号分别为 的 个小球,选出 个小球放入 个盒子中。
考虑钦定剩下的 个小球放入第 个盒子,则该式即为将 个小球放入 个盒子中的方案数,即 。
预处理出 和 的幂次即可做到 。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,M=998244353;
using ll=long long;
using ul=unsigned long long;
void add(int &x,int y) {
(x+=y)>=M&&(x-=M);
}
void add(int &x,ul y,int z) {
x=(y*z+x)%M;
}
int qp(ul a,int x=M-2) {
int res=1;
for(; x; x>>=1,a=a*a%M)
(x&1)&&(res=a*res%M);
return res;
}
long long n,pw[N],ans;
string s;
int main() {
long long number = 0, k = 2;
cin >> s;
for(int i = 0; i < s.size(); i++)
number = (number * 10 + (s[i] - '0')) % M;
reverse(s.begin(),s.end());
pw[0] = 1;
for(int i = 1; i <= s.size(); i++)
pw[i] = 2 * pw[i-1] % M;
for(int i = 0; i < s.size(); i++){
long long now = k * (s[i] - '0') % M * pw[s.size() - i - 1];
k = 11 * k % M;
ans = (ans + now) % M;
}
ans -= number;
if(ans < 0)
ans += M;
cout << ans << endl;
}
作者:yifan0305
出处:https://www.cnblogs.com/yifan0305/p/17773851.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
转载时还请标明出处哟!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】