zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作:
    1.U k a 将序列中第 k 个数修改为 a。
    2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作。
    每次询问独立,即每次询问不会对序列进行修改。

    input
    第一行包含两个正整数 n, m (1<=n, m<=1000000),分别表示序列长度和操作次数。
    接下来 m 行为 m 个操作,其中 1<=k, c<=n,0<=a<=10^9,1<=s<=10^9。

    output
    包含若干行,对于每个 Z 询问,若可行,输出 TAK,否则输出 NIE。

    sample input
    3 8
    U 1 5
    U 2 7
    Z 2 6
    U 3 1
    Z 2 6
    U 2 2
    Z 2 6
    Z 2 1
    sample output
    NIE
    TAK
    NIE
    TAK

    @solution@

    如果序列中的某个数 x > s,因为我们只进行 s 次 -1 的操作,这个数能用到的部分只有 s 这么多。
    所以我们可以将序列中所有大于 s 的数修改成 s。

    询问能够成立的必要条件是序列所有数之和 >= c*s。
    而可以发现,当我们将大于 s 的数修改成 s 过后,如上的条件就变成了充要条件。

    证明的话可以使用归纳法。
    首先可以发现,每次操作至少保留 c 个正数。分类讨论等于 s 的数的个数:
    (1)若等于 s 的数 >= c,显然成立(直接拿这几个数进行操作)。
    (2)若等于 s 的数 < c,我们保证选出的 c 个数包含这些等于 s 的数。这样操作完后依然满足所有数之和 >= c*(s-1)且所有数<=(s-1),就可以归纳证明了。

    至于实现,我们并不需要真的把所有大于 s 的数修改成 s,只需要统计大于 s 的数的个数再乘上 s 即可。

    可以用平衡树维护,也可以用离散化 + 线段树维护。

    @accepted code@

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 1000000;
    struct query{
    	int type, x, y;
    }qry[MAXN + 5];
    int a[MAXN + 5], d[MAXN + 5];
    int n, m, dcnt; 
    void discrete() {
    	sort(d+1, d+dcnt+1);
    	dcnt = unique(d+1, d+dcnt+1) - d - 1;
    	for(int i=1;i<=m;i++)
    		if( !qry[i].type )
    			qry[i].y = lower_bound(d+1, d+dcnt+1, qry[i].y) - d;
    }
    struct node{
    	int le, ri;
    	ll sum; int cnt;
    }tree[4*MAXN + 5];
    void pushup(int x) {
    	tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
    	tree[x].cnt = tree[x<<1].cnt + tree[x<<1|1].cnt;
    }
    void build(int x, int l, int r) {
    	tree[x].le = l, tree[x].ri = r;
    	tree[x].sum = tree[x].cnt = 0;
    	if( l == r ) {
    		if( l == 1 ) tree[x].cnt = n;
    		return ;
    	}
    	int mid = (l + r) >> 1;
    	build(x<<1, l, mid);
    	build(x<<1|1, mid+1, r);
    	pushup(x);
    }
    void modify(int x, int p, int k) {
    	if( p > tree[x].ri || p < tree[x].le )
    		return ;
    	if( tree[x].le == tree[x].ri ) {
    		tree[x].sum += k*d[tree[x].le];
    		tree[x].cnt += k;
    		return ;
    	}
    	modify(x<<1, p, k);
    	modify(x<<1|1, p, k);
    	pushup(x);
    }
    ll s; int c;
    void query(int x, int p) {
    	if( d[tree[x].ri] <= p ) s += tree[x].sum;
    	else if( d[tree[x].le] > p ) c += tree[x].cnt;
    	else query(x<<1, p), query(x<<1|1, p);
    }
    char cmd[2];
    int main() {
    	d[dcnt = 1] = 0;
    	scanf("%d%d", &n, &m);
    	for(int i=1;i<=m;i++) {
    		scanf("%s%d%d", cmd, &qry[i].x, &qry[i].y);
    		if( cmd[0] == 'U' )
    			qry[i].type = 0, d[++dcnt] = qry[i].y;
    		else qry[i].type = 1;
    	}
    	discrete(); build(1, 1, dcnt);
    	for(int i=1;i<=n;i++)
    		a[i] = 1;
    	for(int i=1;i<=m;i++) {
    		if( !qry[i].type ) {
    			modify(1, a[qry[i].x], -1);
    			a[qry[i].x] = qry[i].y;
    			modify(1, a[qry[i].x], 1);
    		}
    		else {
    			s = c = 0; query(1, qry[i].y);
    			puts(1LL*qry[i].x*qry[i].y <= s + 1LL*c*qry[i].y ? "TAK" : "NIE");
    		}
    	}
    }
    

    @details@

    一开始找了一个比较复杂的结论,还涉及到线段树上二分等诡异的操作。
    大概一两个月过后再去看……这什么玩意儿啊……我怎么看不懂我写了啥啊……

    然后翻看题解才看到了这个比较简单的结论。

  • 相关阅读:
    实时27实战机器学习:图片验证码识别(Java实现)
    大屏26深度学习模型来从文档图片中自动化地提取出关键信息成为一项亟待解决的挑战
    f-string想必作为Python3.6版本开始引入的特性,通过它我们可以更加方便地向字符串中嵌入自定义内容
    大屏25JAVA+selenium+tess4j识别登陆验证码截图与识别
    前端12 highcharts和echarts选择
    大屏20基于 Selenium 的 Web 自动化测试框架完美版自动化解决方案 [开源项目]
    大屏24字典python+selenium的行为
    大屏23Tesseract字库训练Tesseract 3
    大屏21解决数据问题python-tesseract-ocr的安装及使用
    大屏22解决数据问题java浏览器源.docx
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10359090.html
Copyright © 2011-2022 走看看