zoukankan      html  css  js  c++  java
  • @codeforces


    @description@

    给定长度为 n 的序列,m 次询问以及参数 p。共有两类询问:

    (1)"1 l r id":将区间 [l, r] 的颜色改成 id。
    (2)"2 l r":对于区间 [l, r],输出不超过 100/p 种颜色,要求在区间内占比超过 >= p% 的颜色都应该被输出。

    点我查看原题

    @solution@

    看了好几遍题意才看懂那个奇怪的输出方法。。。
    其实就是让你找到另一个判定条件,使得题目给的判定条件是你所找的判定条件的充分条件。。。

    考虑 p >= 51 时,该问题是寻找区间占比 > 1/2 的数的充分条件。
    这个问题。。。其实是一个经典问题(网上搜得到很多相关博客)。

    寻找区间占比 > 1/2 的数(如果存在),只需要每次选中 2 个不同的数并同时消去,最后剩下的数就是所寻找的数。
    类似地,可以大胆猜测寻找区间占比 > 1/k 的数(如果存在):只需要每次选中 k 个不同的数并同时消去,最后所剩下的 k-1 个数就是所寻找的数。

    正确性?假如原先有 n 个数,其中 r 个为 x,且 r/n > 1/k。
    假如你所选的 k 个数不包含 x,则新的占比 r/(n - k) > 1/k(这是显然的);否则,新的占比 (r - 1)/(n - k) > 1/k(这也是显然的)。
    即操作一次后占比依然 > 1/k。那么归纳到 n < k 时,就可以得证了。

    注意到只有前提 “存在这样的数”,两个过程才是等价的。
    也就是说找出来的数不一定是区间占比 > 1/k 的数,但区间占比 > 1/k 的数一定会被找出。两者形成充分条件。
    这也就用上了题目那奇怪的条件。

    先找到最小的整数 k 使得 x > 1/k 的充分条件为 x >= p%。

    考虑用数据结构(线段树)加速这一过程:我们对于每个结点只维护区间内删完后剩下的 <= k-1 个数以及它们的出现次数 cnt。
    合并两个区间的信息时,只需要把一个区间的数往另一个区间插入。
    分情况简单讨论一下,当数的种类数 = k 的时候就把这些数的出现次数 cnt 同时减去出现次数最少的数的出现次数 min{cnt}。

    @accepted code@

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    typedef pair<int, int> pii;
    #define mp make_pair
    #define fi first
    #define se second
    
    const int MAXN = 150000;
    
    int n, m, p, q;
    
    struct node{
    	pii a[5]; int s;
    	node() {s = 0;}
    	node(pii x) {a[0] = x, s = 1;}
    	friend node insert(const node &A, const pii &x) {
    		node B = A;
    		for(int i=0;i<B.s;i++)
    			if( B.a[i].se == x.se ) {
    				B.a[i].fi += x.fi;
    				return B;
    			}
    		if( B.s < q )
    			B.a[B.s++] = x;
    		else {
    			int mn = 0;
    			for(int i=0;i<B.s;i++)
    				if( B.a[i].fi < B.a[mn].fi ) mn = i;
    			if( B.a[mn].fi >= x.fi ) {
    				for(int i=0;i<B.s;i++)
    					B.a[i].fi -= x.fi;
    			}
    			else {
    				pii y = B.a[mn]; B.a[mn] = x;
    				for(int i=0;i<B.s;i++)
    					B.a[i].fi -= y.fi;
    			}
    		}
    		return B;
    	}
    	friend node merge(const node &A, const node &B) {
    		if( A.s == 0 ) return B;
    		if( B.s == 0 ) return A;
    		node C = A;
    		for(int i=0;i<B.s;i++)
    			C = insert(C, B.a[i]);
    		return C;
    	}
    };
    
    int a[MAXN + 5];
    struct segtree{
    	#define lch (x << 1)
    	#define rch (x << 1 | 1)
    	
    	int le[4*MAXN + 5], ri[4*MAXN + 5], tg[4*MAXN + 5];
    	node nd[4*MAXN + 5];
    	void pushup(int x) {
    		nd[x] = merge(nd[lch], nd[rch]);
    	}
    	void pushdown(int x) {
    		if( tg[x] ) {
    			nd[lch] = node(mp(ri[lch]-le[lch]+1, tg[x]));
    			nd[rch] = node(mp(ri[rch]-le[rch]+1, tg[x]));
    			tg[lch] = tg[rch] = tg[x], tg[x] = 0;
    		}
    	}
    	void build(int x, int l, int r) {
    		le[x] = l, ri[x] = r, tg[x] = 0;
    		if( l == r ) {
    			nd[x] = node(mp(1, a[l]));
    			return ;
    		}
    		int m = (l + r) >> 1;
    		build(lch, l, m), build(rch, m + 1, r);
    		pushup(x);
    	}
    	void modify(int x, int l, int r, int k) {
    		if( l > ri[x] || r < le[x] )
    			return ;
    		if( l <= le[x] && ri[x] <= r ) {
    			nd[x] = node(mp(ri[x]-le[x]+1, k)), tg[x] = k;
    			return ;
    		}
    		pushdown(x);
    		modify(lch, l, r, k), modify(rch, l, r, k);
    		pushup(x);
    	}
    	node query(int x, int l, int r) {
    		if( l > ri[x] || r < le[x] )
    			return node();
    		if( l <= le[x] && ri[x] <= r )
    			return nd[x];
    		pushdown(x);
    		return merge(query(lch, l, r), query(rch, l, r));
    	}
    }T;
    
    void solve(int l, int r) {
    	node nd = T.query(1, l, r);
    	printf("%d", nd.s);
    	for(int i=0;i<nd.s;i++)
    		printf(" %d", nd.a[i].se);
    	puts("");
    }
    
    int main() {
    	scanf("%d%d%d", &n, &m, &p), q = 100 / p;
    	for(int i=1;i<=n;i++) scanf("%d", &a[i]);
    	T.build(1, 1, n);
    	for(int i=1;i<=m;i++) {
    		int op; scanf("%d", &op);
    		if( op == 1 ) {
    			int l, r, id; scanf("%d%d%d", &l, &r, &id);
    			T.modify(1, l, r, id);
    		}
    		else {
    			int l, r; scanf("%d%d", &l, &r);
    			solve(l, r);
    		}
    	}
    }
    

    @details@

    没错我也开始做集训队作业了。
    什么?集训队作业一共 150 道题?抱歉告辞告辞。

    没见过经典模型的我一开始本来想的是随机抽样选点(大概率选中占比更多的颜色) + 判定,结果不是 WA 就是 TLE。
    这样来来回回 15 次过后(途中还发现系统库的rand函数好像有些点无论选什么种子都找不到)我决定还是看看题解。
    然后我就死了。。。

  • 相关阅读:
    http请求的跨域
    事件驱动模型
    单例设计
    引用类型与垃圾回收
    注解开发
    Java的反射机制
    Android Listener 监听的几种写法
    人生必须有所规划,工作是好比马拉松
    Android 常用的常量
    Android 常见的广播 action常量
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12119404.html
Copyright © 2011-2022 走看看