zoukankan      html  css  js  c++  java
  • SEERC 2019 A.Max or Min

    题意:

    给你一个环序列n个数,每个(a_i)有2个相邻的数,一次操作可以将(a_i)变为它和两个相邻数这三个数中最大的或者最小的。询问将n个数都变成1~m所需要的最小次数。

    解法

    我们先考虑对于一个单一的k,将n个数变成k。此时,有意义的条件只是比k大或者比k小,所以我们用-1表示小于k的数,0表示k,1表示比k大的数。很显然,只要有k就可以将整个序列变成k。
    对于0 1 1或者0 -1 -1这样的,我们挨个将它变成0就可以了。我们需要考虑的是0 -1 1 -1 1这样交替变换的。显然我们需要多一次操作将-1 1变成-1 -1 或者1 1然后依次变成0。一个长度为L的交替变换序列,我们需要多(lfloor {frac{L}{2}} floor)次操作。因此我们将n个数变为k的所需次数(f(k)=num(-1)+num(1)+sum_{L} {lfloor {frac{L}{2}} floor})
    这样我们通过O(n)可以求出一个值,那么k每次加一,就会将k从0变成-1,k+1从1变成0。线段树修改查询就可以在O(mlogn)的复杂度内求出所有答案。

    #include <bits/stdc++.h>
    #define pb emplace_back
    #define lson rt << 1
    #define rson rt << 1 | 1
    using namespace std;
    
    const int maxn = 4e5;
    vector <int> pos[maxn + 11];
    int a[maxn + 11];
    struct node{
    	int l,r,ans;
    }tree[4 * maxn + 11];
    int sta = 0;
    node merge(node A,node B,int l,int r,int mid) {
    	node ret;
    	bool flag = 1ll * (a[mid] - sta) * (a[mid + 1] - sta) < 0 ? true : false;
    	if (A.l == mid - l + 1 && flag) {
    		ret.l = A.l + B.l;
    	}
    	else ret.l = A.l;
    	if (B.r == r - mid && flag) {
    		ret.r = A.r + B.r;
    	}
    	else ret.r = B.r;
    	if (flag)
    		ret.ans = A.ans - A.r / 2 + B.ans - B.l / 2 + (A.r + B.l) / 2;
    	else ret.ans = A.ans + B.ans;
    	return ret;
    } 
    
    void build(int rt,int l,int r) {
    	if (l == r) {
    		if (a[l] == sta) tree[rt] = {0 , 0 , 0};
    		else tree[rt] = {1 , 1 , 0};
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	build(lson , l , mid);
    	build(rson , mid + 1 , r);
    	tree[rt] = merge(tree[lson] , tree[rson] , l , r , mid);
    }
    
    void update(int rt,int l,int r,int pos) {
    	if (l > pos || r < pos) return;
    	if (l == r) {
    		if (a[l] == sta) tree[rt] = {0 , 0 , 0};
    		else tree[rt] = {1 , 1 , 0};
    		return;
    	}
    	int mid = (l + r) >> 1;
    	update(lson , l , mid , pos);
    	update(rson , mid + 1 , r , pos);
    	tree[rt] = merge(tree[lson] , tree[rson] , l , r , mid);
    }
    
    node query(int rt,int l,int r,int al,int ar) {
    	if (l >= al && r <= ar) {
    		return tree[rt];
    	}
    	int mid = (l + r) >> 1;
    	if (mid >= ar) return query(lson , l , mid , al , ar);
    	if (al > mid) return query(rson , mid + 1 , r , al , ar);
    	return merge(query(lson , l , mid , al , ar) , query(rson , mid + 1 , r , al , ar) , max(l , al) , min(r , ar) , mid);
    } 
    
    int main(){
    	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    	int n,m;
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++) {
    		cin >> a[i];
    		pos[a[i]].pb(i);
    	}
    	for (int i = n + 1; i <= 2 * n; i++) a[i] = a[i - n];
    	build(1 , 1 , 2 * n);
    	for (int i = 1; i <= m; i++) {
    		sta = i;
    		for (auto p : pos[i]) {
    			update(1 , 1 , 2 * n , p); update(1 , 1 , 2 * n , p + n);
    		}
    		for (auto p : pos[i - 1]) {
    			update(1 , 1 , 2 * n , p); update(1 , 1 , 2 * n , p + n);
    		}
    		if (pos[i].empty()) {
    			printf("-1 ");
    			continue;
    		}
    		int l = pos[i][0]; int r = pos[i][0] + n;
    		int ret = query(1 , 1 , 2 * n , l , r).ans;
    		printf("%d " , ret + n - (int)pos[i].size());
    	} 
    } 
    
    
  • 相关阅读:
    如何在 Windows 7 上安装 TeX Live 2018
    CF 964C Alternating Sum
    WF 18 A 想法
    CLion 使用笔记
    数理统计学复习
    ssh无密码访问设置(ssh-keygen 的详解)
    mysql开启远程连接的方法
    mysql命令大全
    Linux下创建仓库的软件包createrepo
    Linux 格式化分区 报错Could not stat --- No such file or directory 和 partprobe 命令
  • 原文地址:https://www.cnblogs.com/Embiid/p/12525869.html
Copyright © 2011-2022 走看看