zoukankan      html  css  js  c++  java
  • 【37.07%】【UESTC 360】Another LCIS

    Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
     

    For a sequence S1,S2,,SNS1,S2,⋯,SN, and a pair of integers (i,j)(i,j), if 1ijN1≤i≤j≤N and Si<Si+1<Si+2<<Sj1<SjSi<Si+1<Si+2<⋯<Sj−1<Sj, then the sequence Si,Si+1,,SjSi,Si+1,⋯,Sj is a CIS(Continuous Increasing Subsequence). The longest CIS of a sequence is called the LCIS (Longest Continuous Increasing Subsequence).

    In this problem, we will give you a sequence first, and then some add operations and some query operations. An add operation adds a value to each member in a specified interval. For a query operation, you should output the length of the LCIS of a specified interval.

    Input

    The first line of the input is an integer TT, which stands for the number of test cases you need to solve.

    Every test case begins with two integers NNQQ, where NN is the size of the sequence, and QQ is the number of queries. S1,S2,,SNS1,S2,⋯,SN are specified on the next line, and then QQqueries follow. Every query begins with a character a or qa is followed by three integers LLRRVV, meaning that add VV to members in the interval [L,R][L,R] (including LLRR), and q is followed by two integers LLRR, meaning that you should output the length of the LCIS of interval [L,R][L,R].

    T10T≤10;

    1N,Q1000001≤N,Q≤100000;

    1LRN1≤L≤R≤N;

    10000S1,S2,,SN,V10000−10000≤S1,S2,⋯,SN,V≤10000.

    Output

    For every test case, you should output Case #k: on a single line first, where kk indicates the case number and starts at 11. Then for every q query, output the answer on a single line. See sample for more details.

    Sample input and output

    Sample Input Sample Output
    1
    5 6
    0 1 2 3 4 
    q 1 4
    a 1 2 -10
    a 1 1 -6
    a 5 5 -4
    q 2 3
    q 4 4
    Case #1:
    4
    2
    1

    Source

    The 9th UESTC Programming Contest Preliminary

    【题解】

    给你一个初始序列。

    然后会不定期的问你这个序列在l..r的区间内最长(严格)上升子序列的长度。

    但是还没完。这个序列是会发生改变的。

    会定期把区间内的某一段同时递增或减少一个值。

    要用线段树来解决。

    但是线段树的域应该记录什么呢?

    可以这样想。

    一个区间内的最长上升子序列。要么整个全部在区间的左半部分。要么整个全部在区间的右半部分。

    对不对??

    傻逼

    还有一种情况呢!

    那就是横跨了中间这个域。一部分在左半部分一部分在右半部分。

    是不是和hotel那题很像?

    用llong[rt],rlong[rt]分别表示起点在(rt节点表示的区间的)左端点和终点在(rt节点表示的区间的)右端点的最长上升子序列的长度。

    然后maxlong[rt]表示整个区间内不管在哪里的最长上升子序列的长度。

    然后可以想象一下

    如果rt的左儿子(x)和右儿子(y)他们满足

    x的最右半那个元素<y的最左边那个元素。

    那么就可以串成一个rlong[rt<<1]+llong[rt<<1|1]了。这就是横跨的情况了。

    根据这个思路。我们还要在线段树的域上加上两个标志,分别记录这个节点表示的区间的最左边的那个元素和最右边的那个元素。

    maxlong[rt] = max{maxlong[rt<<1],maxlong[rt<<|1]}

    if (rnum[rt<<1]<lnum[rt<<1|1])

    maxlong[rt] = max{maxlong,rlong[rt<<1]+llong[rt<<1|1]};

    然后就是询问的一些繁琐的事情了。看代码吧。

    【代码】

    #include <cstdio>
    #include <algorithm>
    #define lson begin,m,rt<<1
    #define rson m+1,end,rt<<1|1
    
    using namespace std;
    
    const int MAXN = 100100;
    
    int n, m;
    int maxlong[MAXN*4],llong[MAXN * 4],rlong[MAXN*4],rnum[MAXN*4],lnum[MAXN*4];
    int lazy_tag[MAXN * 4];
    
    void push_up(int rt,int len)//push_up会繁琐一些。
    {
    	bool flag = rnum[rt << 1] < lnum[rt << 1 | 1];
    	maxlong[rt] = max(maxlong[rt<<1], maxlong[rt<<1|1]);
    	if (flag)
    		maxlong[rt] = max(maxlong[rt], rlong[rt << 1] + llong[rt << 1 | 1]);
    	llong[rt] = llong[rt << 1];//左儿子的最左边那段
    	if (llong[rt] == (len - (len >> 1)) && flag)//如果整个左儿子都递增 且flag(自己看是什么)
    		llong[rt] += llong[rt << 1 | 1];//那么就可以加上右儿子的左边部分。
    	rlong[rt] = rlong[rt << 1 | 1];
    	if (rlong[rt] == (len >> 1) && flag)
    		rlong[rt] += rlong[rt << 1];
    	lnum[rt] = lnum[rt << 1];
    	rnum[rt] = rnum[rt << 1 | 1];
    }
    
    void build(int begin, int end, int rt) //建树顺便输入
    {
    	maxlong[rt] = llong[rt] = rlong[rt] = lazy_tag[rt] = 0;
    	if (begin == end)
    	{
    		int x;
    		scanf("%d", &x);
    		llong[rt] = rlong[rt] = maxlong[rt] = 1;
    		rnum[rt] = lnum[rt] = x;
    		return;
    	}
    	int m = (begin + end) >> 1;
    	build(lson);
    	build(rson);
    	push_up(rt,end-begin+1);
    }
    
    void input_data()
    {
    	scanf("%d%d", &n, &m);
    	build(1, n, 1);
    }
    
    void push_down(int rt)//处理懒惰标记
    {
    	if (lazy_tag[rt] != 0)
    	{
    		lazy_tag[rt << 1] += lazy_tag[rt];
    		lazy_tag[rt << 1 | 1] += lazy_tag[rt];
    		lnum[rt << 1] += lazy_tag[rt];
    		rnum[rt << 1] += lazy_tag[rt];
    		lnum[rt << 1 | 1] += lazy_tag[rt];
    		rnum[rt << 1 | 1] += lazy_tag[rt];
    		lazy_tag[rt] = 0;
    	}
    }
    
    void up_data(int l, int r, int num, int begin, int end, int rt)
    {
    	if (l <= begin && end <= r)
    	{//递增的时候最长上升序列不会变。因为是一整段同时递增
    		lazy_tag[rt] += num;
    		lnum[rt] += num;
    		rnum[rt] += num;
    		return;
    	}
    	push_down(rt);
    	int m = (begin + end) >> 1;
    	if (l <= m)
    		up_data(l, r, num, lson);
    	if (m < r)
    		up_data(l, r, num, rson);
    	push_up(rt, end - begin + 1);
    }
    
    int query(int l, int r, int begin, int end, int rt)//询问也有点复杂
    {
    	if (l <= begin && end <= r)
    		return maxlong[rt];
    	push_down(rt);
    	int m = (begin + end) >> 1;
    	int temp = 0;
    	bool flag1 = false, flag2 = false;
    	if (l <= m)
    	{
    		temp = max(temp, query(l, r, lson));
    		flag1 = true;
    	}
    	if (m < r)
    	{
    		temp = max(temp, query(l, r, rson));
    		flag2 = true;
    	}
    	if (flag1 && flag2 && rnum[rt << 1] < lnum[rt << 1 | 1]) //如果有左半部分、右半部分。且能够串起来
    	{
    		int left = min(m - l + 1, rlong[rt << 1]);//不能超过询问的区间长度
    		int right = min(r - m, llong[rt << 1 | 1]);
    		temp = max(temp, left + right);//加起来
    	}
    	return temp;
    }
    
    void output_ans()
    {
    	for (int i = 1; i <= m; i++)
    	{
    		char op[5];
    		int x, y, z;
    		scanf("%s", op);
    		if (op[0] == 'a')
    		{
    			scanf("%d%d%d", &x, &y, &z);
    			up_data(x, y, z,1, n, 1);
    		}
    		else
    			if (op[0] == 'q')
    			{
    				scanf("%d%d", &x, &y);
    				printf("%d
    ", query(x, y, 1, n, 1));
    			}
    	}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	//freopen("F:\rush_out.txt", "w", stdout);
    	int t;
    	scanf("%d", &t);
    	for (int ii = 1; ii <= t; ii++)
    	{
    		printf("Case #%d:
    ",ii);
    		input_data();
    		output_ans();
    	}
    	return 0;
    }


  • 相关阅读:
    servlet中如何实现通过Spring实现对象的注入
    异步Socket
    JAVA NIO实现
    【Java并发】
    JAVA实现阻塞队列
    lock与synchronized比较
    线程执行顺序
    ConcurrentHashMap 1.8
    LeetCode 416 分割等和子集
    linux常用指令
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632265.html
Copyright © 2011-2022 走看看