【】2024.5.19

2024.5.19 【人啊…想要保护重要东西的时候,就真的能变得很坚强。】

Sunday 四月十二


模拟赛

A. 楼兰图腾

在完成了分配任务之后,西部 314 来到了楼兰古城的西部。

相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(∧),他们分别用 V 和 ∧ 的形状来代表各自部落的图腾。

西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。

西部 314 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn)
,其中 y1∼yn是 1 到 n 的一个排列。

西部 314打算研究这幅壁画中包含着多少个图腾。

如果三个点 (i,yi),(j,yj),(k,yk)满足 1≤i<j<k≤n且 yi>yj,yj<yk,则称这三个点构成 V 图腾;

如果三个点 (i,yi),(j,yj),(k,yk)满足 1≤i<j<k≤n且 yi<yj,yj>yk,则称这三个点构成 ∧ 图腾;

西部 314想知道,这 n个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和 ∧ 的个数。

输入格式
第一行一个数 n

第二行是 n个数,分别代表 y1,y2,…,yn。

输出格式
两个数,中间用空格隔开,依次为 V 的个数和 ∧ 的个数。

数据范围
对于所有数据,n≤200000
,且输出答案不会超过 int64

y1∼yn是 1 到 n 的一个排列。

输入样例:

5
1 5 3 2 4

输出样例:

3 4

树状数组求解逆序对,

就是当前数字前比他小的总数,

离散化后,

用当前数位减去前面小的就是后面小的。

前面总数减去前面小的就是前面大的,

组合求解即可。

//2024.5.19
//by white_ice
#include<bits/stdc++.h>
using namespace std;
#define itn long long
#define int long long
constexpr int oo = 200005;

int minout;
int maxout;

struct node{int val,num,id;}a[oo];
bool cmp (node x,node y){return x.val>y.val;}
bool cmp1(node a,node b){return a.val<b.val;}

int n;
int c[oo];
void add(int x,int k){for(int i=x;i<=n;i+=i&(-i)) c[i]+=k;}
int query(int x){
    int sum=0;
    for(int i=x;i>0;i-=i&(-i)) sum+=c[i];
    return sum;}

int ans;
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].val),a[i].num=i;

    sort(a+1,a+n+1,cmp1);
    for (itn i=1;i<=n;i++)
        a[i].id = i;

    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        int maxp;
        itn minp;
        int k = a[i].id;
        add(a[i].num,1);
        maxp=query(a[i].num-1);
        minp=a[i].num-1-maxp;
        //cout << minp <<' '<<k-1-minp<< ' '<< maxp<<' '<< n-k-maxp << endl;
        minout += minp*(k-1-minp);
        maxout += maxp*(n-k-maxp);
    }
    cout << maxout << ' '<<minout;
    return 0;
}

附pb_ds抽象做法

//Zn
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define int long long
using namespace std;
using namespace __gnu_pbds;
inline int read()
{
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N=2e5+10;
tree<int,null_type,
    std::less<int>,
    splay_tree_tag,
    tree_order_statistics_node_update>ll,rl;
tree<int,null_type,
    std::greater<int>,
    splay_tree_tag,
    tree_order_statistics_node_update>lg,rg;
int n,a[N],l_l[N],r_l[N],l_g[N],r_g[N];
int ans1,ans2;
signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=n;i++)
    {
        ll.insert(a[i]);
        l_l[i]=ll.order_of_key(a[i]);
    }
    for(int i=n;i>=1;i--)
    {
        rl.insert(a[i]);
        r_l[i]=rl.order_of_key(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        lg.insert(a[i]);
        l_g[i]=lg.order_of_key(a[i]);
    }
    for(int i=n;i>=1;i--)
    {
        rg.insert(a[i]);
        r_g[i]=rg.order_of_key(a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        ans1+=l_g[i]*r_g[i];
        ans2+=l_l[i]*r_l[i];
    }
    printf("%lld %lld",ans1,ans2);
    return 0;
}

B. 谜一样的牛(Lost Cows)

题目描述

有 n 头奶牛,已知它们的身高为 1∼n 且各不相同,但不知道每头奶牛的具体身高。

现在这 n 头奶牛站成一列,已知第 i 头牛前面有 A i A_i Ai头牛比它低,求每头奶牛的身高。

输入格式
第 1 行:输入整数 n。

第 2…n 行:每行输入一个整数 A i A_i Ai ,第 i 行表示第 i 头牛前面有 A i A_i Ai头牛比它低。 (注意:因为第 1 头牛前面没有牛,所以并没有将它列出)

输出格式
输出包含 n 行,每行输出一个整数表示牛的身高。

第 i 行输出第 i 头牛的身高。

数据范围
1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105

输入样例:

5
1
2
1
0

输出样例:

2
4
5
3
1

仍然是树状数组求逆序对

二分依次求解答案即可

//2024.5.19
//by white_ice
#include<bits/stdc++.h>
using namespace std;
#define itn int
constexpr int oo = 100005;

itn bitt(itn x){return x&(-x);}

int st[oo];

int n;
int tree[oo];
itn out[oo];

void add(int x,int k){for(int i=x;i<=n;i+=bitt(i))tree[i]+=k;}
int query(int x){
    int sum=0;for(int i=x;i>0;i-=(bitt(i)))sum+=tree[i];
    return sum;}

int ans;

int main(){

    scanf("%d",&n);

    for (itn i=1;i<=n;i++)add(i,1);

    for(int i=2;i<=n;i++)
        scanf("%d",&st[i]);

    st[1] = 0;
    for(int i=n;i>=1;i--){
        int l = 1,r = n;
        int ans;
        st[i]++;
        while(l<=r){
            int mid=(l+r)>>1;
            if(query(mid)>=st[i]){
                ans = mid;
                r = mid-1;
            }
            else
                l = mid+1;
        }
        out[i] = ans;
        add(ans,-1);
    }

    for (itn i=1;i<=n;i++)
        cout << out[i] << endl;

    return 0;
}

再附上pb_ds抽象做法…

//Zn
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
const int N=1e5+10;
tree<int,null_type,
    std::less<int>,
    splay_tree_tag,
    tree_order_statistics_node_update>tr;
int n,a[N],b[N];
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		tr.insert(i);
	for(int i=n;i>=2;i--)
	{
		b[i]=*tr.find_by_order(a[i]);
		tr.erase(tr.find_by_order(a[i]));
	}
	b[1]=*tr.find_by_order(0);
	for(int i=1;i<=n;i++)
		printf("%d\n",b[i]);
    return 0;
}

C. 区间最大公约数(Interval GCD)

题目描述

给定一个长度为

N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:

C l r d,表示把 A[l],A[l+1],…,A[r] 都加上 d.

Q l r,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数 N,M。

第二行 N 个整数 A[i]。

接下来 M 行表示 M 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围
$$
N≤500000,M≤100000,

1≤A[i]≤10_18 ∣d∣≤10_18
$$
,保证数据在计算过程中不会超过 long long 范围。

输入样例:

5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4

输出样例:

1
2
4

更相减损法告诉我们,

加上一个数后,

我们知道,加上的共同部分减掉了


#include <cmath>
#include <cstdio>
#include <iostream>
#define int long long
 
const int N = 500005;
struct Node {
    int l, r;
    long long ans;
} tree[N << 2];
int a[N], b[N], c[N];
 
long long cnt;
 
long long gcd(long long x, long long y) {
    return y ? gcd(y, x % y) : x;
}
 
namespace SegmentTree {
    inline void build(int rt, int l, int r) {
        tree[rt].l = l, tree[rt].r = r;
        if(l == r) {
            tree[rt].ans = b[l];
            return ;
        }
        
        int mid = (l + r) >> 1;
        build(rt * 2, l, mid);
        build(rt * 2 + 1, mid + 1, r);
        tree[rt].ans = gcd(tree[rt * 2].ans, tree[rt * 2 + 1].ans);
    }
    
    inline void change_point(int rt, int x, int k) {
        if(tree[rt].l == tree[rt].r) {
            tree[rt].ans += k;
            return ;
        }
        
        int mid = (tree[rt].l + tree[rt].r) >> 1;
        if(mid >= x) change_point(rt * 2, x, k);
        else change_point(rt * 2 + 1, x, k);
        tree[rt].ans = gcd(tree[rt * 2].ans, tree[rt * 2 + 1].ans);
    }
    
    inline void ask(int rt, int l, int r) {
        if(tree[rt].l >= l && tree[rt].r <= r) {
            cnt = gcd(cnt, tree[rt].ans);
            return ;
        }
        
        int mid = (tree[rt].l + tree[rt].r) >> 1;
        if(l <= mid) ask(rt * 2, l, r);
        if(r > mid) ask(rt * 2 + 1, l, r);
    }
}
 
int n;
namespace BIT {
    int lowbit(int x) {
        return x & (-x);
    }
    
    inline void add(int x, int k) {
        while(x <= n) {
            c[x] += k;
            x += lowbit(x);
        }
    }
    
    int ask(int x) {
        long long res = 0;
        while(x) {
            res += c[x];
            x -= lowbit(x);
        } return res;
    }
}
 
signed main() {
    static int m;
    scanf("%lld %lld", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
        b[i] = a[i] - a[i - 1];
    } SegmentTree::build(1, 1, n);
    
    char c[3];
    for (int i = 1; i <= m; i++) {
        scanf("%s", c + 1);
        if(c[1] == 'C') {
            int l, r, d;
            scanf("%lld %lld %lld", &l, &r, &d);
            SegmentTree::change_point(1, l, d);
            BIT::add(l, d);
            if(r + 1 <= n) {
                SegmentTree::change_point(1, r + 1, -d);
                BIT::add(r + 1, -d);
            }
        } else {
            cnt = 0; int l, r;
            scanf("%lld %lld", &l, &r);
            SegmentTree::ask(1, l + 1, r);
            printf("%lld\n", gcd(a[l] + BIT::ask(l), std::abs(cnt)));
        }
    } return 0;
}
#代码转载自https://www.cnblogs.com/Nicoppa/p/11590181.html
  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值