JMU第1届程序设计金秋赛题解

  • JMU第一届程序设计金秋赛题解

出题表:
在这里插入图片描述

签到题

5+5

出题人:贝

题意

主人公 5 5 5年前 5 5 5岁, 5 5 5年后几岁?

思路

这是一道小学数学题,要求选手画线段图(bushi)。

因为主人公 5 5 5年前 5 5 5岁,说明现在是 10 10 10岁,也就是说 5 5 5年后 15 15 15岁。

代码实现

#include<stdio.h>
int main()
{
    printf("15");
}

4514

出题人:贝

题意

45 45 45枚硬币在桌上, 14 14 14枚正面朝上,贝贝蒙住了你的眼睛,你无法通过触觉摸出硬币的正反面,现在贝贝要求你执行以下两种操作各一次(两种操作的顺序不限),使得最后左右两堆的硬币正面朝上的一样多:

  1. 将硬币分成左、右两堆,且左边的硬币数量小于右边的硬币数量(因为你的眼睛被蒙住了,所以每一堆硬币的正反面是随机的)。
  2. 将某一堆全部的硬币的正反面都翻转过来(若在未执行1操作时执行该操作,则将桌面所有硬币都翻转正反面)

假设执行1操作时,将硬币分成了数量分别为 a , b a,b a,b的两堆,其中 1 ≤ a ≤ 22 < b ≤ 44 1\le a\le 22< b \le 44 1a22<b44 a + b = 45 a+b=45 a+b=45,贝贝希望你能告诉他 a a a的值是多少?

思路

首先执行1操作,将硬币分为两堆,假设左边有 x x x个,右边则有 45 − x 45-x 45x

设左边的正面朝上的数量为 y y y,根据题意可以得到右边有 14 − y 14-y 14y个正面朝上

然后我们将左边的所有硬币翻面,也就是正面数量等价之前的反面数量 x − y x-y xy

最后需要让二者相等,即 x − y = 14 − y x-y=14-y xy=14y,易得 x = 14 x=14 x=14

故最后输出14即可

当假设题意中的正面朝上数量是给定的一个数 a a a也行,将上述推导中的 14 14 14换成 a a a即可,最后的输出为 a a a。为了降低难度,怕选手雀氏不会做,所以给定了指定的数字14,让选手可以提交若干发一定正确。但是我又不能将提交的次数设置的过小,否则就对于想正解的选手不公平。

代码实现

#include<stdio.h>
int main()
{
    printf("14");
}

小W的砖块

出题人:吴

题意

做两次区间染色,求颜色相交的区间长度。即求两个区间的公共长度。

思路1

因为区间范围给的比较小,可以直接模拟两次染色然后统计答案。多组数据记得清空数组。

时间复杂度 O ( n ) O(n) O(n)

代码实现1

#include<bits/stdc++.h>
using namespace std;

int col[1005];

void solve(){
    memset(col,0,sizeof col);
    int l1,r1,l2,r2;
    cin>>l1>>r1>>l2>>r2;
    for(int i=l1;i<=r1;i++) col[i]=1; // 第一次染色
    int ans=0;
    for(int i=l2;i<=r2;i++) if(col[i]) ans++; // 第二次并不需要真的去染色, 统计一下答案即可
    cout<<ans<<endl;
}

signed main(){
    int _;cin>>_;
    for(int cas=1;cas<=_;cas++){
        solve();
    }
    
    return 0;
}

思路2

不妨设 l 1 ≤ l 2 l_1 \le l_2 l1l2,分三种情况讨论一下。

  • 两个区间不相交,即 r 1 < l 2 r_1<l_2 r1<l2。答案应该为0。
  • 一个区间完全包含于另一个区间,即 r 2 ≤ r 1 r_2 \le r_1 r2r1。答案应该为小区间长度,即 r 2 − l 2 + 1 r_2-l_2+1 r2l2+1
  • 两个区间部分相交。答案应该为相交部分长度,即 r 1 − l 2 + 1 r_1-l_2+1 r1l2+1

时间复杂度 O ( 1 ) O(1) O(1)

画图更清晰哦,注意设置前提条件 l 1 ≤ l 2 l_1 \le l_2 l1l2

代码实现2

#include<bits/stdc++.h>
using namespace std;

void solve(){
    int l1,r1,l2,r2;cin>>l1>>r1>>l2>>r2;
    if(l1>l2){
        swap(l1,l2);
        swap(r1,r2);
    }
    if(r1<l2) cout<<0<<endl; // 区间不相交
    else if(r2<=r1) cout<<r2-l2+1<<endl; // 区间完全包含
    else cout<<r1-l2+1<<endl; // 部分相交
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _;cin>>_;
    while(_--) solve();
    
    return 0;
}

正常作息的贝贝

出题人:周

签到题,按题意模拟即可。

#include <bits/stdc++.h>
using namespace std;
void solve()
{
    int n;
    cin >> n;
    int h = n / 60;
    int m = n % 60;
    printf("%02d:%02d\n", (22 + h) % 24, m);
}
int main()
{
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

简单题

擅长算术的琪露诺

出题人:吴

感谢周大佬写的题面orz

题意

给定一个长度为 n n n 的数组 a [ ] a[] a[],求
∑ i = 1 n ∑ j = 1 n a i ∗ a j \sum_{i=1}^{n}\sum_{j=1}^{n}a_i*a_j i=1nj=1naiaj

思路

∑ i = 1 n ∑ j = 1 n a i ∗ a j = ∑ i = 1 n a i ∑ j = 1 n a j = ( ∑ i = 1 n a i ) ∗ ( ∑ j = 1 n a j ) = ( ∑ i = 1 n a i ) 2 \begin{align} \sum_{i=1}^{n}\sum_{j=1}^{n}a_i*a_j &= \sum_{i=1}^{n}a_i\sum_{j=1}^{n}a_j \\ &= (\sum_{i=1}^{n}a_i)*(\sum_{j=1}^{n}a_j) \\ &= (\sum_{i=1}^{n}a_i)^2 \end{align} i=1nj=1naiaj=i=1naij=1naj=(i=1nai)(j=1naj)=(i=1nai)2

于是答案就是和的平方。时间复杂度 O ( n ) O(n) O(n)

代码实现

#include<bits/stdc++.h>
using namespace std;

void solve(){
    int n;cin>>n;
    long long ans=0;
    for(int i=1;i<=n;i++){
        long long x;cin>>x;
        ans+=x;
    }
    cout<<ans*ans<<endl;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _;cin>>_;
    while(_--) solve();
    
    return 0;
}

中档题

上帝造裸题的七分钟

出题人:周

  • 值得注意的是,因为模数 p p p不一定是质数,所以很难求出逆元,故本题需避免求逆元,以及用除法
  • 对于萌新选手,此处给予提醒,double会有精度误差,只能保留15-16位的有效数字,所以也不能用double

p r e [ i ] pre[i] pre[i]表示第 1 1 1个数到第 i i i个数的积, s u f [ i ] suf[i] suf[i]表示第 i i i个数到第 n n n个数的积。同时令 p r e [ 0 ] = 1 , s u f [ n + 1 ] = 1 pre[0]=1,suf[n+1] = 1 pre[0]=1,suf[n+1]=1

那么 b [ i ] b[i] b[i]就可以表示成

b [ i ] = s u f [ i + 1 ] × p r e [ i − 1 ] b[i] = suf[i+1]\times pre[i-1] b[i]=suf[i+1]×pre[i1]

直接计算即可(注意计算时需要取模)。时间复杂度为 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define endl '\n'
void solve()
{
    int n, p;
    cin >> n >> p;
    vector<LL> a(n + 2), pre(n + 2), suf(n + 2);
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    pre[0] = 1;
    suf[n + 1] = 1;
    for (int i = 1; i <= n; ++i)
        pre[i] = pre[i - 1] * a[i] % p;
    for (int i = n; i >= 1; --i)
        suf[i] = suf[i + 1] * a[i] % p;
    for (int i = 1; i <= n; ++i)
        cout << pre[i - 1] % p * suf[i + 1] % p << " ";
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    solve();
    return 0;
}

静神的外卖红包

出题人:吴

题意

给一个不包含0的数字串,删去 m m m 个数,求能得到的字典序最小串。

思路

不难看出,如果第 i i i 位数字大于第 i + 1 i+1 i+1 位数字,删掉第 i i i 位数字能让数字串变小且最优。

证明:

设数字串为 S S S。若 S 1 > S 2 S_1>S_2 S1>S2,删掉 S 1 S_1 S1 得到的数字串字典序一定比删除 S 2 S_2 S2 得到的数字串字典序小。(删掉 S 1 S_1 S1 的串为 T 1 = S 2 S 3 S 4 . . . S n T_1=S_2S_3S_4...S_n T1=S2S3S4...Sn,删掉 S 2 S_2 S2 的串变为 T 2 = S 1 S 3 S 4 . . . S n T_2=S_1S_3S_4...S_n T2=S1S3S4...Sn,又 S 2 < S 1 S_2<S_1 S2<S1,故 T 1 < T 2 T_1<T_2 T1<T2)。

假设数字串前 i − 1 i-1 i1 位已经构造完成,即数字串前 i − 1 i-1 i1 个数字是字典序最小的串的开头。此时 S i > S i + 1 S_i >S_{i+1} Si>Si+1,删除第 i i i 位后数字串变为 T 1 = S 1 S 2 . . . S i − 1 S i + 1 S i + 2 . . . S n T_1=S_1S_2...S_{i-1}S_{i+1}S_{i+2}...S_n T1=S1S2...Si1Si+1Si+2...Sn,删除第 i + 1 i+1 i+1 位后数字串变为 T 2 = S 1 S 2 . . . S i − 1 S i S i + 2 . . . S n T_2=S_1S_2...S_{i-1}S_{i}S_{i+2}...S_n T2=S1S2...Si1SiSi+2...Sn。由于 T 1 T_1 T1 T 2 T_2 T2 的前 i − 1 i-1 i1 位相同,两串的第 i i i 位为 S i + 1 S_{i+1} Si+1 S i S_i Si,故 T 1 < T 2 T_1<T_2 T1<T2

以上证明可能不太严谨,严谨的证明可百度删数问题。

然后我们只要从左往右找,如果当前数字大于前面的数字,就把前面大于当前的数字都删掉。因为需要找到前面还未被删除的数字,所以拿一个栈或队列来保存和更新答案。

具体做法:遍历数字串,比较栈顶数字(前面还未被删除的数字)和当前数字,弹出栈顶数字直到当前数字大于栈顶数字(或空)。可以发现,最后答案一定是非严格上升的,所以最后若还能继续删除 k k k 数字,删掉最后 k k k 个数字最优。

时间复杂度 O ( n ) O(n) O(n)

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;

char s[N];
char stk[N];

void solve(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>s[i];

    int top=0;
    for(int i=1;i<=n;i++){
        if(top==0) stk[++top]=s[i];
        else{
            while(top && stk[top]>s[i] && m){ // 维护非严格上升的数字串
                top--;
                m--;
            }
            stk[++top]=s[i];
        }
    }
    while(m--){ // 删除最后m个数字
        top--;
    }
    for(int i=1;i<=top;i++) cout<<stk[i];
    cout<<endl;
}

int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _;cin>>_;
    while(_--) solve();
    
    return 0;
}

烦恼的小 h

出题人:周

为了比较好的解决这个问题,下面引入一些在这题特有的符号

  1. 二元组 ( u , v ) (u,v) (u,v)表示节点 u u u到节点 v v v的异或和
  2. ∘ \circ 表示异或符号

下面给出一些显然的性质(不作证明)

  1. ( u , u ) = 0 (u,u) = 0 (u,u)=0
  2. ( u , v ) = ( v , u ) (u,v)= (v,u) (u,v)=(v,u)
  3. ( u , w ) ∘ ( w , v ) = ( u , v ) (u,w)\circ ( w, v) = (u ,v ) (u,w)(w,v)=(u,v)

解法一

题意要求我们求出若干组 ( u , v ) (u,v) (u,v)。我们尝试预处理一些东西来使我们更好的计算它们

l c a lca lca为它们的最近公共祖先,此时我们有

( u , v ) = ( u , l c a ) ∘ ( l c a , v ) (u,v) = (u , lca)\circ (lca ,v) (u,v)=(u,lca)(lca,v)

于是预处理 l c a lca lca 每次找到它们的最近公共祖先后倍增地向上跳即可。时间复杂度 O ( n log ⁡ n + m log ⁡ n ) O(n\log n + m\log n) O(nlogn+mlogn)

参考代码 (下面的代码时间复杂度是 O ( n log ⁡ 2 n O(n\log^2n O(nlog2n的 也能通过)

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define endl '\n'
const int maxn = 2e5 + 10;
vector<pair<int, int>> G[maxn];
int anc[maxn][20 + 3];
int val[maxn][20 + 3];
int de[maxn];
void dfs(int u, int fa)
{
    for (int i = 1; i <= 20; ++i)
    {
        val[u][i] = (val[u][i - 1] ^ val[anc[u][i - 1]][i - 1]);
        anc[u][i] = anc[anc[u][i - 1]][i - 1];
    }
    for (auto [w, to] : G[u])
    {
        if (to == fa)
            continue;
        de[to] = de[u] + 1;
        anc[to][0] = u;
        val[to][0] = w;
        dfs(to, u);
    }
}
int getlca(int x, int y)
{
    if (de[x] < de[y])
        swap(x, y);
    for (int i = 20; i >= 0; i--)
    {
        if (de[anc[x][i]] >= de[y])
            x = anc[x][i];
    }
    if (x == y)
        return x;
    for (int i = 20; i >= 0; --i)
    {
        if (anc[x][i] != anc[y][i])
        {
            x = anc[x][i];
            y = anc[y][i];
        }
    }
    return anc[x][0];
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i < n; ++i)
    {
        int u, v, w;
        cin >> u >> v >> w;
        G[u].push_back({w, v});
        G[v].push_back({w, u});
    }
    int cnt = 0;
    de[1] = 1;
    dfs(1, -1);
    int q;
    cin >> q;
    while (q--)
    {
        int u, v;
        cin >> u >> v;
        int lca = getlca(u, v);
        int ans = 0;
        int res1 = 0;
        for (int i = 20; i >= 0; --i)
        {
            if (getlca(anc[u][i], lca) == lca)
            {
                res1 ^= (val[u][i]);
                u = anc[u][i];
            }
        }
        int res2 = 0;
        for (int i = 20; i >= 0; --i)
        {
            if (getlca(anc[v][i], lca) == lca)
            {
                res2 ^= (val[v][i]);
                v = anc[v][i];
            }
        }
        ans = res1 ^ res2;
        cout << ans << endl;
    }
    return 0;
}

解法二

上述的解法并没有很好的利用异或的性质,它仅仅只是利用了异或的结合律。而异或还有一个重要的性质:异或的逆运算是它本身。 而利用这一点,我们可以把时间复杂度降到 O ( n + m ) O(n+m) O(n+m)

考虑我们需要计算的东西 ( u , v ) (u,v) (u,v)

我们利用上述的三个性质来推导一些东西

( u , v ) = ( u , l c a ) ∘ ( l c a , v ) ∘ 0 = ( u , l c a ) ∘ ( l c a , v ) ∘ [ ( 1 , l c a ) ∘ ( l c a , 1 ) ] = [ ( u , l c a ) ∘ ( l c a , 1 ) ] ∘ [ ( 1 , l c a ) ∘ ( l c a , v ) ] = ( u , 1 ) ∘ ( 1 , v ) = ( 1 , u ) ∘ ( 1 , v ) \begin{aligned} &( u , v) \\ =&(u,lca) \circ (lca, v)\circ 0 \\ = &(u , lca) \circ ( lca , v ) \circ [(1, lca) \circ ( lca, 1 )]\\ = & [(u,lca) \circ ( lca, 1 ) ] \circ [(1, lca) \circ (lca, v)] \\ = & (u, 1) \circ (1, v )\\ = &( 1, u) \circ (1, v) \end{aligned} =====(u,v)(u,lca)(lca,v)0(u,lca)(lca,v)[(1,lca)(lca,1)][(u,lca)(lca,1)][(1,lca)(lca,v)](u,1)(1,v)(1,u)(1,v)

也就是说,我们只需要计算出 1 1 1到所有节点的异或值。然后询问时,把二者的异或值异或起来就是答案。

参考代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<vector<pair<int, int>>> g(n + 1);
    for (int i = 1; i < n; ++i)
    {
        int u, v, w;
        cin >> u >> v >> w;
        g[u].push_back({w, v});
        g[v].push_back({w, u});
    }
    vector<int> f(n + 1);
    function<void(int, int)> dfs = [&](int u, int fa) -> void
    {
        for (auto [w, to] : g[u])
        {
            if (to == fa)
                continue;
            f[to] = f[u] ^ w;
            dfs(to, u);
        }
    };
    dfs(1, -1);
    int q;
    cin >> q;
    while (q--)
    {
        int u, v;
        cin >> u >> v;
        cout << (f[u] ^ f[v]) << endl;
    }
    return 0;
}

难题

烤盐的曾同学

出题人:周

题解太长我不看版:第 n + 2 n+2 n+2项斐波那契数 − 1 -1 1 为答案 矩阵快速幂加速即可

f ( i ) f(i) f(i)为智慧值为 i i i的鱼的个数,那么也就是说

a n s = ∑ i = 1 n f ( i ) ans = \sum_{i =1 }^{n} f(i) ans=i=1nf(i)

其中 f ( i ) f(i) f(i)满足

f ( i ) { 1  if  x = 1 1  if  x = 2 f ( i − 1 ) + f ( i − 2 ) otherwise f(i)\begin{cases} 1 & \text{ if } x=1 \\ 1 & \text{ if } x= 2\\ f(i-1 ) + f( i -2 ) & \text{otherwise} \end{cases} f(i) 11f(i1)+f(i2) if x=1 if x=2otherwise

前两个式子是显然的,而第三个可以这么解释:智慧值为 i i i只能是智慧值为 i − 1 i-1 i1的左节点和智慧值为 i − 2 i-2 i2的右节点。显然,他是一个斐波那契数列。

利用这个式子预处理后计算就可以通过 60 % 60\% 60%的测试点。

下面将描述 80 % 80\% 80%的做法和 100 % 100\% 100%的做法。

80 % 80\% 80%的做法

1 0 9 10^9 109在本地需要跑 4 4 4s 左右,我们可以考虑分块打表来加速这一过程

在本地跑一个隔 1 0 6 10^6 106就输出一次的表,每次查询的时候可以快速找到它所在的块 然后剩余的部分暴力跑一遍即可。

时间复杂度为 O ( 能过 ) O(能过) O(能过)

满分做法

分块打表并没有利用上斐波那契的性质,故不能得到满分。

考虑我们要算的东西

a n s = ∑ i = 1 n f ( i ) ans =\sum_{i =1 }^{ n} f(i) ans=i=1nf(i)

为了下面的讨论方便,这里设 n n n为大于 2 2 2的奇数(偶数同理)。

将这个东西按奇偶分类,有

a n s = f ( 1 ) + f ( 3 ) + f ( 5 ) + ⋯ + f ( n ) + f ( 2 ) + f ( 4 ) + f ( 6 ) + ⋯ + f ( n − 1 ) = [ f ( 2 ) + f ( 3 ) + f ( 5 ) + ⋯ + f ( n ) ] + [ f ( 1 ) + f ( 2 ) + f ( 4 ) + f ( 6 ) + ⋯ + f ( n − 1 ) ] − f ( 1 ) = f ( n + 1 ) + f ( n ) − f ( 1 ) = f ( n + 2 ) − 1 \begin{aligned} ans =& f(1) + f(3) + f(5) + \cdots + f(n) \\ +&f(2) + f(4) + f(6) + \cdots + f(n -1 ) \\ = & [f(2) + f(3) + f(5) + \cdots + f(n)] \\ +&[f(1) +f(2) + f(4) + f(6) + \cdots + f(n -1 ) ]- f(1) \\ = & f( n +1) + f(n) -f(1) \\ =& f(n +2 ) -1 \end{aligned} ans=+=+==f(1)+f(3)+f(5)++f(n)f(2)+f(4)+f(6)++f(n1)[f(2)+f(3)+f(5)++f(n)][f(1)+f(2)+f(4)+f(6)++f(n1)]f(1)f(n+1)+f(n)f(1)f(n+2)1

单项的斐波那契数列可以用矩阵快速幂在 O ( log ⁡ n ) O(\log n) O(logn)的时间复杂度内跑出。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
constexpr int P = 998244353;
using i64 = long long;
// assume -P <= x < 2P
int norm(int x)
{
    if (x < 0){x += P;}
    if (x >= P){x -= P;}
    return x;
}
template <class T>
T power(T a, int b)
{
    T res = 1;
    for (; b; b /= 2, a *= a){if (b % 2){res *= a;}}
    return res;
}
struct Z
{
    int x;
    Z(int x = 0) : x(norm(x)) {}
    int val() const{return x;}
    Z operator-() const{return Z(norm(P - x));}
    Z inv() const{assert(x != 0);return power(*this, P - 2);}
    Z &operator*=(const Z &rhs){x = i64(x) * rhs.x % P;return *this;}
    Z &operator+=(const Z &rhs){x = norm(x + rhs.x);return *this;}
    Z &operator-=(const Z &rhs){x = norm(x - rhs.x);return *this;}
    Z &operator/=(const Z &rhs){return *this *= rhs.inv();}
    friend Z operator*(const Z &lhs, const Z &rhs){Z res = lhs;res *= rhs;return res;}
    friend Z operator+(const Z &lhs, const Z &rhs){Z res = lhs;res += rhs;return res;}
    friend Z operator-(const Z &lhs, const Z &rhs){Z res = lhs;res -= rhs;return res;}
    friend Z operator/(const Z &lhs, const Z &rhs){Z res = lhs;res /= rhs;return res;}
    friend std::istream &operator>>(std::istream &is, Z &a){i64 v;is >> v;a = Z(v);return is;}
    friend std::ostream &operator<<(std::ostream &os, const Z &a){return os << a.val();}
};
template <class T>
struct Matrix
{
    int siz1, siz2;
    vector<vector<T>> v;
    Matrix(int n) : siz1(n), siz2(n), v(n, vector<T>(n)){for (int i = 0; i < n; ++i)v[i][i] = T(1);}
    Matrix(int n, int m) : siz1(n), siz2(m), v(n, vector<T>(m)) {} //声明两个参数可以不把它化成单位阵
    vector<T> &operator[](int i) { return v[i]; }
    Matrix operator*(Matrix const &rhs) const
    {
        assert(rhs.siz1 == siz2);
        Matrix<T> ans(siz1, rhs.siz2);
        int siz = siz2;
        for (int i = 0; i < ans.siz1; ++i)
            for (int j = 0; j < ans.siz2; ++j)
                for (int k = 0; k < siz; ++k)
                {ans.v[i][j] += v[i][k] * rhs.v[k][j];}
        return ans;
    }
    Matrix qpow(long long b) const
    {
        assert(siz1 == siz2);
        Matrix ans(siz1);
        Matrix res = *this;
        while (b)
        {
            if (b & 1) ans = ans * res;
            res = res * res;
            b /= 2;
        }
        return ans;
    }
};
void solve()
{
    LL n;
    cin >> n;
    Matrix<Z> a(2, 2), b(2, 2);
    a[0][0] = a[0][1] = a[1][0] = 1;
    b[0][0] = b[0][1] = 1;
    auto ans = b * a.qpow(n);
    cout << ans[0][0] - 1 << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

死去的2021突然攻击我

出题人:贝

题意

对于给定的正整数 a , b , c , d a,b,c,d a,b,c,d,求满足 x × y x\times y x×y能被 2021 2021 2021整除的数对 ( x , y ) (x,y) (x,y)的数量,其中 a ≤ x ≤ b , c ≤ y ≤ d a\le x \le b, c\le y \le d axb,cyd

思路

代码实现

算法思路

  • 容斥原理(计数问题经常见)
区间之间的容斥
  • 我们要求的是[a,b][c,d]中满足条件的数对,如图所示
    在这里插入图片描述

  • 计算区间[1,b][1,d]满足题目条件的数对个数
    在这里插入图片描述

  • 再减去[1,a-1][1,d]中满足的,减去[1,b][1,c-1]中满足的,
    在这里插入图片描述

在这里插入图片描述

  • 容斥原理得,[1,a-1]对应[1,c-1]的数对会被多减去一次,所以要加回来
    在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mw08BGpa-1664353175545)(C:\Users\GhostLX\AppData\Roaming\Typora\typora-user-images\image-20220922093012343.png)]

  • 那么我们最后得到的就是[a,b][c,d]中满足题目的数对关系了,因为求解两个区间满足条件的数对关系代码一直反复,所以我们可以写一个函数(思路想清楚再打代码,可以减小代码的冗余量以及bug率)
区间内的容斥:求解数对关系
  • 思路进一步推进,我们的问题已经简化为求解[1,x][1,y]中满足题目条件的数对个数
  • 我们可以发现2021可以分解为43*47,所以问题就被划分成了一下几步
  1. 此处即[1,x]这个区间为A,[1,y]这个区间为B
  2. A的因子出2021,B的因子出任意 and A的因子出任意,B的因子出2021,即·x/2021)*y+x*(y/2021)·
  3. 两边同时出2021的情况会算两次,减去该情况,即(x/2021)*(y/2021)
  4. 计算A出47和B出43的情况,此时单出2021的情况已经被计算过了,即(x/47-x/2021)*(y/43-y/2021)
  5. 计算A出43,B出47的情况,(x/43-x/2021)*(y/47-y/2021)

代码实现

#include <bits/stdc++.h>
using namespace std;
// 2021=43*47
typedef long long LL;

LL cal(LL x, LL y)
{ //计算[1,x]和[1,y]中满足题目条件的数对个数
    //此处即[1,x]这个区间为A,[1,y]这个区间为B
    LL cnt = 0;
    cnt += (x / 2021) * y + x * (y / 2021);           // A的因子出因子2021,B的因子出任意因子 and A的因子出任意因子,B的因子出因子2021
    cnt -= (x / 2021) * (y / 2021);                   //上式中,两边同时出2021的情况会算两次,此处减去
    cnt += (x / 47 - x / 2021) * (y / 43 - y / 2021); //单出2021的情况已经被计算过了,计算A出47和B出43的情况
    cnt += (x / 43 - x / 2021) * (y / 47 - y / 2021); //计算A出43,B出47的情况
    return cnt;
}

long long findPairs(long long a, long long b, long long c, long long d)
{
    return cal(b, d) - cal(a - 1, d) - cal(b, c - 1) + cal(a - 1, c - 1);
    //计算区间[1,b]和[1,d]满足题目条件的数对个数
    //再减去[1,a-1]和[1,d]中满足的,减去[1,b]中满足的
    //容斥原理得,[a-1,c-1]这个区间会被多减去一次,所以要加回来
}

int main()
{
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    cout << findPairs(a, b, c, d);
}

压轴

二分战神

出题人:贝

题意

  • a [ ] a[] a[]数组中,任意一个长度大于 k k k的子串的第 k k k大的数放入 b [ ] b[] b[]数组中(不去重),求解 b b b数组中第 m m m大的数

思路

  • 尺取法+二分答案
  • 二分第 m m m大的数的值 m i d mid mid,如果 b [ ] b[] b[]数组中比 m i d mid mid大的值 < m <m <m则说明 m i d mid mid比答案大需要减小右边界,反之增大
  • 那接下来的问题就是如何用尺取法计算 b [ ] b[] b[]数组中比 m i d mid mid大的值
  • 定义左边界 i i i和右边界 j j j s u m sum sum记录当前区间比 m i d mid mid大的数的个数, a n s ans ans记录函数的答案
  • 先将长度达到 k k k,再继续增加 s u m sum sum,直至 s u m = k sum=k sum=k,此时后面的所有区间的 [ i , k ] , k ∈ [ j + 1 , n ] [i,k],k\in [j + 1, n] [i,k],k[j+1,n],取出的第 k k k大的数字都 > m i d >mid >mid,所以这些数在 b [ ] b[] b[]中一定大于 m i d mid mid,此时ans += n - j + 1
  • 注意枚举右指针时, j < n j<n j<n,且增加 i i i时候,记得根据 a [ i ] a[i] a[i]是否 > m i d >mid >mid来判断 s u m sum sum是否需要减1

代码实现

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 1e5 + 10, md = 1e9 + 7;

LL a[N], n, k, m;

LL check(int mid) //尺取法判断
{
    LL j = 0, sum = 0, ans = 0;
    // j代表右指针,sum代表当前区间中大于mid的元素个数
    for (int i = 1; i <= n; i++)
    {
        //如果区间长度<k,并且下标还没有越界
        while (j - i + 1 < k && j < n) 
        {
            j++;
            if (a[j] > mid)
                sum++; //比 mid大的数加一
        }
        while (sum < k && j < n) //区间长度从k继续增加
        {
            j++;
            if (a[j] > mid)
                sum++;
        }
        if (sum < k) //说明j==n的时候sum都<k
            break;//则接下来没有任何一个区间会有贡献
        ans += (n - j + 1); //[i,k], 其中k in [j + 1, n]
        //这些区间的k th 都必然 > mid

        if (a[i] > mid) //下一个区间不包括这个i位置
            sum--;
    }
    return ans; //比mid大的b数组元素个数
}

int main()
{
    cin.tie(0);
    cout.tie(0), cin.sync_with_stdio(false);

    int T;
    cin >> T;
    while (T--)
    {
        cin >> n >> k >> m;
        for (int i = 1; i <= n; i++)
            cin >> a[i];

        int str = 0, end = 1e9;
        while (str <= end) //二分答案
        {
            int mid = (str + end) / 2;
            if (check(mid) < m) //长度大于k的区间中,比mid大元素的数量<m
            {//这样的方案才合法,才有可能是答案
                end = mid - 1;
            }
            else
            {
                str = mid + 1;
            }
        }
        cout << str << endl;
    }
}

我回来了

出题人:吴

题意

给你一个序列 a a a 让你支持

  1. [ l , r ] [l,r] [lr] 区间赋值

  2. [ l , r ] [l,r] [lr] 询问区间最小值

但我们不会直接给你序列 a a a,而是给你序列一个长度为 n n n 的序列 b b b ,把 b b b 复制粘贴 k k k 次就可以得到 a a a

思路

值域 1 e 9 1e9 1e9,考虑线段树动态开点,但是初始化朴树建树时间复杂度为 O ( n k ∗ l o g 2 ( n k ) ) O(nk*log_2(nk)) O(nklog2(nk)) 会T。

假设原数组从 0 0 0 开始。

首先线段树里需要维护区间最小值,考虑开点时如何初始化一个节点的区间最小值(区间 [ l , r ] [l,r] [l,r] ):

  • r − l + 1 > = n r-l+1>=n rl+1>=n,那么区间最小值一定是 b b b 数组的最小值。
  • r − l + 1 < n r-l+1<n rl+1<n,且 ⌊ l n ⌋ = ⌊ r n ⌋ \lfloor \frac{l}{n} \rfloor = \lfloor \frac{r}{n} \rfloor nl=nr,即 [ l % n , r % n ] [l\%n,r\%n] [l%n,r%n] b b b 中的一段,此时主要查找 b b b 数组中 [ l % n , r % n ] [l\%n,r\%n] [l%n,r%n]的最小值。
  • r − l + 1 < n r-l+1<n rl+1<n,且 ⌊ l n ⌋ ≠ ⌊ r n ⌋ \lfloor \frac{l}{n} \rfloor \ne \lfloor \frac{r}{n} \rfloor nl=nr,那么需要取 [ l % n , n − 1 ] [l\%n,n-1] [l%n,n1] [ 0 , r % n ] [0,r\%n] [0,r%n] 的最小值。
  • 上述查询可以对 b b b 数组建立ST表或线段树完成查询。(因为每次开点的时候都是在原序列的基础上获取信息)

剩下的就是动态开点板子了。

使用ST表时间复杂度为 O ( n ∗ l o g 2 n + q ∗ l o g 2 ( n ∗ k ) ) O(n*log_2n+q*log_2(n*k)) O(nlog2n+qlog2(nk))

代码实现

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;

int n,k;
int a[N];
namespace ST{
    int logn[N];
    void pre(){
        logn[1]=0,logn[2]=1;
        for(int i=3;i<N;i++) logn[i]=logn[i/2]+1;
    }
    int st[N][21];
    void init(int n){
        pre();
        for(int i=0;i<n;i++) st[i][0]=a[i];
        for(int j=1;j<=20;j++){
            for(int i=0;i+(1<<j)-1<n;i++){
                st[i][j]=min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int query_min(int l,int r){
        int k=logn[r-l+1];
        return min(st[l][k], st[r-(1<<k)+1][k]);
    }
};
namespace Segment_Tree{
    const int inf=1e9+10;
    int tot=0;
    struct node{
        int ls,rs;
        int minn;
        int same;
    }tr[N*55];
    void make_node(int& x,int l,int r){
        if(x) return;
        x=++tot;
        tr[x].same=-1;
        if(r-l+1>=n) tr[x].minn=ST::query_min(0,n-1);
        else if(l/n == r/n) tr[x].minn=ST::query_min(l%n, r%n);
        else tr[x].minn=min(ST::query_min(0,r%n), ST::query_min(l%n,n-1));
    }
    void do_same(int& u,int ul,int ur,int x){
        tr[u].minn=x;
        tr[u].same=x;
    }
    void pushdown(int& u,int ul,int ur){
        int mid=ul+ur>>1;
        make_node(tr[u].ls,ul,mid);
        make_node(tr[u].rs,mid+1,ur);
        if(~tr[u].same){
            do_same(tr[u].ls,ul,mid,tr[u].same);
            do_same(tr[u].rs,mid+1,ur,tr[u].same);
            tr[u].same=-1;
        }
    }
    void pushup(int& u){
        tr[u].minn=min(tr[tr[u].ls].minn, tr[tr[u].rs].minn);
    }
    void update_same(int& u,int ul,int ur,int l,int r,int x){
        make_node(u,ul,ur);
        if(l<=ul && ur<=r){
            do_same(u,ul,ur,x);
            return;
        }
        pushdown(u,ul,ur);
        int mid=ul+ur>>1;
        if(l<=mid) update_same(tr[u].ls, ul, mid, l, r, x);
        if(mid<r) update_same(tr[u].rs, mid+1, ur, l, r, x);
        pushup(u);
    }
    int query_min(int& u,int ul,int ur,int l,int r){
        make_node(u,ul,ur);
        if(l<=ul && ur<=r) return tr[u].minn;
        pushdown(u,ul,ur);
        int mid=ul+ur>>1;
        int res=inf;
        if(l<=mid) res=min(res, query_min(tr[u].ls, ul, mid, l, r));
        if(mid<r) res=min(res, query_min(tr[u].rs, mid+1, ur, l, r));
        return res;
    }
};

void solve(){
    cin>>n>>k;
    for(int i=0;i<n;i++) cin>>a[i];
    ST::init(n);
    int root=0;
    Segment_Tree::make_node(root,0,n-1);
    int q;cin>>q;
    while(q--){
        int op;cin>>op;
        if(op==1){
            int l,r,x;cin>>l>>r>>x;
            l--;r--;
            Segment_Tree::update_same(root,0,n*k-1,l,r,x);
        }
        else{
            int l,r;cin>>l>>r;
            l--;r--;
            cout<<Segment_Tree::query_min(root,0,n*k-1,l,r)<<endl;
        }
    }
}

int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    solve();
    
    return 0;
}

另外本题写ODT可以拿到 80 % 80\% 80% 的分数(因为实在是卡不动了悲)。

验题人题解

建立三类线段树:

  1. 原始数据,区间长度为N
  2. 每个复制出来的块,建立一棵线段树,区间长度为K
  3. 块中具体的每个位置,动态开点建立线段树

于是我花了2h写出了400行的代码才过了这题(出题人过于毒瘤)

#include <bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << x << '\n';
#define bd cerr << "----------------------" << el;
#define el '\n'
#define cl putchar('\n');
#define pb push_back
#define eb emplace_back
#define x first
#define y second
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define lop(i, a, b) for (int i = (a); i < (b); i++)
#define dwn(i, a, b) for (int i = (a); i >= (b); i--)
#define ceil(a, b) (a + (b - 1)) / b
#define ms(a, x) memset(a, x, sizeof(a))
#define INF 0x3f3f3f3f
#define db double
#define all(x) x.begin(), x.end()
#define reps(i, x) for (int i = 0; i < x.size(); i++)
#define cmax(a, b) a = max(a, b)
#define cmin(a, b) a = min(a, b)
#define mpa make_pair

typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<db, db> PDD;
typedef vector<int> vci;

struct read
{
    static const int M = 1 << 21;
    char buf[M], *S = buf, *P = buf, c, f;
    inline char getc()
    {
        return (S == P && (P = (S = buf) + fread(buf, 1, 1 << 21, stdin), S == P) ? EOF : *S++);
    }
    template <typename T>
    read &operator>>(T &x)
    {
        for (c = 0; !isdigit(c); c = getc())
            f = c;
        for (x = 0; isdigit(c); c = getc())
            x = x * 10 + (c - '0');
        return x = (f ^ '-') ? x : -x, *this;
    }
} fin;

constexpr int N = 1e5 + 10, M = 4 * N, B = 66, md = 1e9 + 7;
const double PI = acos(-1), eps = 1e-8;

int T, n, m, k;
int b[N];
int initMi, initMx;

struct NodeS
{
    int mi;
    int tag;
} seg[M], s[M];

//原线段树
void buildS(int u, int l, int r)
{
    s[u].tag = -1;
    if (l == r)
    {
        s[u].mi = b[l];
        return;
    }
    int mid = l + r >> 1;
    buildS(u << 1, l, mid);
    buildS(u << 1 | 1, mid + 1, r);
    s[u].mi = min(s[u << 1].mi, s[u << 1 | 1].mi);
}

#define lson(u) (u) << 1, l, mid
#define rson(u) (u) << 1 | 1, mid + 1, r

int queryS(int u, int l, int r, int ql, int qr)
{
    if (ql <= l && r <= qr)
    {
        return s[u].mi;
    }
    int mid = l + r >> 1;
    if (qr <= mid) //左半边
        return queryS(lson(u), ql, qr);
    else if (ql > mid) //右半边
        return queryS(rson(u), ql, qr);
    else
    {
        int lans = queryS(lson(u), ql, qr);
        int rans = queryS(rson(u), ql, qr);
        return min(lans, rans);
    }
}
int queryS(int ql, int qr)
{
    return queryS(1, 1, n, ql, qr);
}

//动态开点 线段树
inline PII getKuai(int i)
{ // x块的
    return mpa((i - 1) * n + 1, i * n);
}
inline int getL(int i)
{
    return (i - 1) * n + 1;
}
inline int getR(int i)
{
    return i * n;
}
inline int getK(int l)
{
    return (l - 1) / n + 1;
}
// [(i - 1)n + 1, i n]

struct Node
{
    int L, R; //左右指针
    int mi;
    int tag;
    int su; //对应的原始线段树的节点
} dy[N * 64];
int tot;
int kToDy[N * 4];
void buildDy(int u)
{
    dy[u] = {-1, -1, initMi, -1, 1};
}

void pushDownDy(int u, int son)
{
    if (dy[u].tag != -1)
    {
        dy[son].tag = dy[u].tag;
        dy[son].mi = dy[u].tag;
    }
}

void pushUpDy(int u)
{
    int tmp1, tmp2;
    if (~dy[u].L)
        tmp1 = dy[dy[u].L].mi;
    else {
//        if(~dy[u].tag)
//            tmp1 = dy[u].tag;
//        else
            tmp1 = s[dy[u].su << 1].mi;
    }
    if (~dy[u].R)
        tmp2 = dy[dy[u].R].mi;
    else {
//        if(~dy[u].tag)
//            tmp2 = dy[u].tag;
//        else
            tmp2 = s[dy[u].su << 1 | 1].mi;

    }


    dy[u].mi = min(tmp1, tmp2);
}

int queryDy(int u, int l, int r, int ql, int qr)
{                   //查询的一定不是完整的
    if (~dy[u].tag) //存在tag,则说明整个区间全是一个数
        //此时查询区间交集的最小值,也一定是这个值
        return dy[u].mi;
    if (ql <= l && r <= qr)
    {
        return dy[u].mi;
    }
    int mid = l + r >> 1;
    int tmp = INT_MAX;
    if (ql <= mid)
    {
        if (!~dy[u].L)
        { //不存在左儿子的时候,访问原本的子树
            cmin(tmp, queryS(dy[u].su << 1, l, mid, ql, qr));
        }
        else
        {
            pushDownDy(u, dy[u].L);
            cmin(tmp, queryDy(dy[u].L, l, mid, ql, qr));
        }
    }
    if (qr > mid)
    {
        if (!~dy[u].R)
        { //不存在右儿子的时候
            cmin(tmp, queryS(dy[u].su << 1 | 1, mid + 1, r, ql, qr));
        }
        else
        {
            pushDownDy(u, dy[u].R);
            cmin(tmp, queryDy(dy[u].R, mid + 1, r, ql, qr));
        }
    }
    if(~dy[u].L && ~dy[u].R)
        dy[u].tag = -1; // tag一定下传完毕
    return tmp;
}

int queryDy(int u, int ql, int qr)
{
    return queryDy(u, 1, n, ql, qr);
}

void mapSToDy(int u, int su)
{ //将s[su]的节点信息赋值给dy[u]
    dy[u].su = su;
    dy[u].mi = s[su].mi;
    dy[u].tag = -1;
    dy[u].L = dy[u].R = -1;
}

void modifyDy(int u, int l, int r, int ml, int mr, int x)
{
    if (ml <= l && r <= mr)
    {
        dy[u].mi = dy[u].tag = x;
        return;
    }
    int mid = l + r >> 1;
    if(~dy[u].tag)
    {
        if (!~dy[u].L)
        { //不存在左儿子的时候
            int L = dy[u].L = ++tot;
            mapSToDy(L, dy[u].su << 1);
        }
        if (!~dy[u].R)
        { //不存在左儿子的时候
            int R = dy[u].R = ++tot;
            mapSToDy(R, dy[u].su << 1 | 1);
        }
        pushDownDy(u, dy[u].L);
        pushDownDy(u, dy[u].R);

        dy[u].tag = -1;
    }
    if (ml <= mid)
    {
        if (!~dy[u].L)
        { //不存在左儿子的时候
            int L = dy[u].L = ++tot;
            mapSToDy(L, dy[u].su << 1);
        }
        modifyDy(dy[u].L, l, mid, ml, mr, x);
    }
    if (mr > mid)
    {
        if (!~dy[u].R)
        { //不存在左儿子的时候
            int R = dy[u].R = ++tot;
            mapSToDy(R, dy[u].su << 1 | 1);
        }
        modifyDy(dy[u].R, mid + 1, r, ml, mr, x);
    }
    pushUpDy(u);    //修改的话,需要pushUP
    if(~dy[u].L && ~dy[u].R)
        dy[u].tag = -1; // tag一定下传完毕

}

//分块 线段树
void buildSeg(int u, int l, int r)
{
    seg[u].tag = -1;
    if (l == r)
    {
        seg[u].mi = initMi;
        kToDy[l] = ++tot;
        buildDy(kToDy[l]);
        return;
    }
    int mid = l + r >> 1;
    buildSeg(u << 1, l, mid);
    buildSeg(u << 1 | 1, mid + 1, r);
    seg[u].mi = min(seg[u << 1].mi, seg[u << 1 | 1].mi);
}

PII intersection(int l1, int r1, int l2, int r2)
{
    return mpa(max(l1, l2), min(r1, r2));
}

int toMod(int x)
{
    return (x - 1) % n + 1;
}

void pushUpSeg(int u)
{
    seg[u].mi = min(seg[u << 1].mi, seg[u << 1 | 1].mi);
}

void pushDownSeg(int u)
{
    if (~seg[u].tag)
    {
        seg[u << 1].tag = seg[u << 1].mi = seg[u].tag;
        seg[u << 1 | 1].tag = seg[u << 1 | 1].mi = seg[u].tag;
        seg[u].tag = -1;
    }
}

int querySeg(int u, int l, int r, int ql, int qr, int kl, int kr)
{
    if (l == r)
    {
        PII k = getKuai(l);
        if (ql <= k.x && k.y <= qr)
            return seg[u].mi;
        PII jiao = intersection(k.x, k.y, ql, qr);
        if (~seg[u].tag)
        {
            dy[kToDy[l]].mi = dy[kToDy[l]].tag = seg[u].tag;
            seg[u].tag = -1;
        }
        return queryDy(kToDy[l], toMod(jiao.x), toMod(jiao.y));
    }
    int kx = getL(l);
    int ky = getR(r);
    if (ql <= kx && ky <= qr)
        return seg[u].mi;
    pushDownSeg(u);
    int mid = l + r >> 1;
    if (kr <= mid) //左半边
        return querySeg(lson(u), ql, qr, kl, kr);
    else if (kl > mid) //右半边
        return querySeg(rson(u), ql, qr, kl, kr);
    else
    {
        int lans = querySeg(lson(u), ql, qr, kl, kr);
        int rans = querySeg(rson(u), ql, qr, kl, kr);
        return min(lans, rans);
    }
}

int querySeg(int ql, int qr)
{
    int kl = getK(ql);
    int kr = getK(qr);
    return querySeg(1, 1, k, ql, qr, kl, kr);
}

void modifySeg(int u, int l, int r, int ml, int mr, int x, int kl, int kr)
{
    if (l == r)
    {
        PII k = getKuai(l);
        PII jiao = intersection(k.x, k.y, ml, mr);
        if (~seg[u].tag)
        {
            dy[kToDy[l]].mi = dy[kToDy[l]].tag = seg[u].tag;
            seg[u].tag = -1;
        }
        modifyDy(kToDy[l], 1, n, toMod(jiao.x), toMod(jiao.y), x);
        seg[u].mi = dy[kToDy[l]].mi;
        return;
    }
    int kx = getL(l);
    int ky = getR(r);
    if (ml <= kx && ky <= mr)
    {
        seg[u].mi = seg[u].tag = x;
        return;
    }
    int mid = l + r >> 1;
    pushDownSeg(u);
    if (kl <= mid) //左半边
        modifySeg(lson(u), ml, mr, x, kl, kr);
    if (kr > mid) //右半边
        modifySeg(rson(u), ml, mr, x, kl, kr);
    pushUpSeg(u);
}

void modifySeg(int ml, int mr, int x)
{
    int kl = getK(ml);
    int kr = getK(mr);
    modifySeg(1, 1, k, ml, mr, x, kl, kr);
}
// #define fin cin
int main()
{
    fin >> n >> k;
    rep(i, 1, n)
    {
        fin >> b[i];
    }
    //建立原始的线段树
    buildS(1, 1, n);
    initMi = queryS(1, n); //初始最小值
    buildSeg(1, 1, k);
    int op, l, r, x;
    // rep(i, 1, 2 * n - 1)
    // {
    //     cerr << i << el;
    //     cerr << s[i].mi << ' ' << el;
    //     bd;
    // }
    fin >> T;

    while (T--)
    {
        fin >> op;
        if (op == 1)
        {
            fin >> l >> r >> x;
            modifySeg(l, r, x);
        }
        else
        {
            fin >> l >> r;
            cout << querySeg(l, r) << el;
        }
                // debug(op);
    }
    // cout << queryS(1, n, 0) << ' ' << queryS(1, 1, 1) << ' ' << queryS(1, n, 1);
}
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值