Loading

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

同色三角形

题目描述

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

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

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

输入描述:

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

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

输出描述:

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

示例1

输入

4
1 2
2 1
2 1
3 0

输出

1

说明

备注:

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

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

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

对于 \(50\%\) 的数据,有 \(n \le 20\)

对于 \(70\%\) 的数据,有 \(n \le 1000\)

对于 \(100\%\) 的数据,有 \(3 \le n \le 3 \times 10^5\)

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

总的方案:\(C_{n}^{3}\),异色方案:\(\dfrac{\sum_{i = 1}^{n}(a_i \times b_i)}{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\) 个数字记为 \(a_i\)​。

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

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

操作类型 \(0\):给定 \(0,pos,val\),表示将 \(a_{pos}\)​ 修改为 \(val\)

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

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

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

输入描述:

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

接下来 \(n\) 行,分别表示所有的 \(s_i\)

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

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

输出描述:

对于每个操作 \(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\%\) 的数据,有 \(1 \le n,q \le 10,1 \le a_i​,|s_i​| \le 5\)

对于 \(50\%\) 的数据,有 \(1 \le n,q \le 10^3,\sum|s_i​| \le 1000\)

对于 \(70\%\) 的数据,有 \(1 \le n,a_i \le 10^5,1 \le q \le 5 \times 10^5,1 \le |s_i​| \le 10^3\)

对于 \(100\%\) 的数据,有 \(1 \le n,a_i \le 10^5,1 \le q \le 5 \times 10^5,\sum |s_i​|≤3 \times 10^5\)

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

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

所以我们发现答案只与 \(|s_i​|\) 有关,与 \(s\) 内容无关。如果处理好这些 \(f\) 的话,将可以获得 \(50 \sim 70\) 的暴力分。

100pt

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

因为 \(\sum |s_i​| \le 3 \times 10^5\),所以 \(|s_i|​\) 至多有 \(\sqrt{6 \times 10^5}\)​ 种。那么我们就可以维护一个单点修改,区间查询乘积的线段树了。

#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 \in [10^{m−1},10^m)\)\(n\) 从高到低位记为 \(a_{m−1}\)​ 到 \(a_0\)​,即\(n = a_{m−1} ​a_{m−2}​ \dots a_0\)​。记 \(S\)\(\{0,1,2,⋯,m−1\}\) 的子集。每个 \(S\) 对应特定的 \(a\) 的子序列,即 \(S=\{0,2,3\}\),则 \(x=a_3​a_2​a_0\)​。两个方案不同等价于 \(S\) 不同。

输入描述:

输入包含一行。

第一行输入一个正整数 \(n\)

输出描述:

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

示例1

输入

123

输出

231

说明

\(1\) 个数位:\(12+3=15,23+1=24,13+2=15\)

\(2\) 个数位:\(3+12=15,1+23=24,2+13=15\)

\(3\) 个数位:\(0+123=123\)

全部求和得到 \(231\)

示例2

输入

221

输出

359

备注:

  • 对于测试点 \(1\)\(1 \le n < 10^{100}\)

  • 对于测试点 \(2 \sim 5\)\(1 \le n < 10^{1000}\)

  • 对于测试点 \(6∼10\)\(1 \le n < 10^{100000}\)

60 分暴力:

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

\[ans = \sum_{i = 1}^{n} 2^i a_i \sum_{j = 0}^{n - i} (C_{n - i}^{j} \times 10^{j}) - 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\) 构成双射,则最终答案即为 \(2 \sum x−原数\)

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

\[2^{i - 1} a_i \sum_{j = 0}^{n - i} 10^j \dbinom{n - i}{j} \]

考虑这个 \(\sum_{j = 0}^{n - i} 10^j \dbinom{n - i}{j}\) 式子的组合意义,记 \(x\),即为给出标号分别为 \(1 \sim x\) 的 \(x\) 个小球,选出 \(j\) 个小球放入 \(10\) 个盒子中。

考虑钦定剩下的 \(x−j\) 个小球放入第 \(11\) 个盒子,则该式即为将 \(x\) 个小球放入 \(11\) 个盒子中的方案数,即 \(11^{n−i}\)

预处理出 \(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; 
}
posted @ 2023-10-19 08:33  yi_fan0305  阅读(89)  评论(0编辑  收藏  举报