「解题报告」2023-10-18 模拟赛

同色三角形#

题目描述#

有一个 n 个点的完全图,点分别被编号为 1n,每个点都有 n1 条边连向其它点。每条边有绿色或者红色两种颜色,现在我们知道每个点连着几条绿色边,几条红色边,但不知道每条边具体连接哪个点。

在完全图中任选三个点,观察它们之间的三条边,如果三条边颜色相同,那么我们就称其为“同色三角形”。现在牛牛已经知道了每个点连着几条绿色、红色边(保证这样的图一定存在),他想要知道图中最多有几个同色三角形。

很可惜,牛牛是色盲,所以这个问题只能交给你了。

输入描述:#

第一行输入一个正整数 n,表示共有 n 个点。

接下来包含 n 行,每行给两个数字 a,b,其中第 i 行的数字表示第 i 个点的红色边、绿色边数量,保证 a+b=n1

输出描述:#

输出一行一个整数表示答案。

示例1

输入#

4
1 2
2 1
2 1
3 0

输出#

1

说明#

备注:#

对于 10% 的数据,有 n=3

对于 20% 的数据,有 n=4

对于 30% 的数据,有 n=5

对于 50% 的数据,有 n20

对于 70% 的数据,有 n1000

对于 100% 的数据,有 3n3×105

容斥原理,用总的方案减去异色的方案。

总的方案:Cn3,异色方案:i=1n(ai×bi)2

// 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;
}

任务#

白浅妹妹有 n 个任务和一个长度为 n 的数组 a,数组中的第 i 个数字记为 ai​。

白浅妹妹的第 i 个任务是用一个字符串 si​ 描述的,意思是需要构造一个长度为 ai​ 的字符串 S,使得 si​ 是 S 的子序列。

现在给定 q 次操作,每次给出三个正整数。其中第一个正整数为 01,表示操作类型。

操作类型 0:给定 0,pos,val,表示将 apos​ 修改为 val

操作类型 1:给定 1,l,r,表示询问白浅妹妹从第 l 个任务一直完成到第 r 个任务,她总共有多少种方案构造字符串。

请注意,在白浅妹妹完成 lr 这些字符串的过程的若干方案中,只要有任何一个任务构造的字符串不同,我们则认为这是不同的方案。

由于答案可能很大,请输出答案对 998244353 取模之后的结果。

输入描述:#

第一行输入两个正整数 n,q

接下来 n 行,分别表示所有的 si

接下来一行给出 n 个由空格隔开的正整数,表示数组 a

接下来 q 行,每行给出三个正整数,意义如题面所示。三个正整数中的第一个正整数为 01,表示两种操作。

输出描述:#

对于每个操作 1,输出一行一个正整数表示答案对 998244353 取模之后的结果。

示例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

备注:#

对于 20% 的数据,有 1n,q101ai,|si|5

对于 50% 的数据,有 1n,q103,|si|1000

对于 70% 的数据,有 1n,ai105,1q5×105,1|si|103

对于 100% 的数据,有 1n,ai105,1q5×105,|si|3×105

我们考虑如何求出包含 s 这个子序列的长为 L 的字符串 S 有多少个。令 f(i,j)​ 表示目前枚举到了 S 的第 j 位,匹配到了 s 的第 i 位的方案数。那么 f(i,j)

(要么第 j 位匹配上了,那么 j 的取值就只有一种,要么第 j 位没有匹配上,那么取值就有 25 个)

所以我们发现答案只与 |si| 有关,与 s 内容无关。如果处理好这些 f 的话,将可以获得 5070 的暴力分。

100pt

通过大眼观察法,我们发现 alt

因为 |si|3×105,所以 |si| 至多有 6×105​ 种。那么我们就可以维护一个单点修改,区间查询乘积的线段树了。

#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笔试面试备考平台_牛客网
来源:牛客网

题目描述#

白浅妹妹正在学习加法,但是老师只给了她一个数字 n,她没法对一个数字做加法运算,于是她从 n 中取出若干个数位(至少 1 个),然后按照原来的相对顺序拼接组成新一个数字 x,剩余的数位也按原来的相对顺序组成另一个数字 y,将两个数字 x,y 求和。

例如 12345 可以拿出 24,剩下 135,求和 24+135=159

求所有拆数字的方案的和对 998244353 取模后的结果。

允许把 n 中所有的数位全部取出,此时 n 变成 0

n[10m1,10m)n 从高到低位记为 am1​ 到 a0​,即n=am1am2a0​。记 S{0,1,2,,m1} 的子集。每个 S 对应特定的 a 的子序列,即 S={0,2,3},则 x=a3a2a0​。两个方案不同等价于 S 不同。

输入描述:#

输入包含一行。

第一行输入一个正整数 n

输出描述:#

输出一个数,即所有拆数字的方案的和对 998244353 取模后的结果。

示例1

输入#

123

输出#

231

说明#

1 个数位:12+3=1523+1=2413+2=15

2 个数位:3+12=151+23=242+13=15

3 个数位:0+123=123

全部求和得到 231

示例2

输入#

221

输出#

359

备注:#

  • 对于测试点 11n<10100

  • 对于测试点 251n<101000

  • 对于测试点 6101n<10100000

60 分暴力:#

可以发现答案即为这个式子:

ans=i=1n2iaij=0ni(Cnij×10j)a

// 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;
}

发现若 x 可以为空,则 x 可以和 y 构成双射,则最终答案即为 2x

考虑求 x,可以考虑每一位的贡献,则有从高到低第 i 位的贡献为

2i1aij=0ni10j(nij)

考虑这个 j=0ni10j(nij) 式子的组合意义,记 x,即为给出标号分别为 1x 的 x 个小球,选出 j 个小球放入 10 个盒子中。

考虑钦定剩下的 xj 个小球放入第 11 个盒子,则该式即为将 x 个小球放入 11 个盒子中的方案数,即 11ni

预处理出 2 和 11 的幂次即可做到 O(n)

#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 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(151)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示