zoukankan      html  css  js  c++  java
  • 【50.40%】【BZOJ 4553】[Tjoi2016&Heoi2016]序列

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 371  Solved: 187
    [Submit][Status][Discuss]

    Description

     佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

    可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你
    ,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可
    。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:
    1 2 3
    2 2 3
    1 3 3
    1 1 31 2 4
    选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列
    为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求

    Input

     输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的

    状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。所有数字均为正整数
    ,且小于等于100,000

    Output

     一个整数

    Sample Input

    3 4
    1 2 3
    1 2
    2 3
    2 1
    3 4

    Sample Output

    3

    【题解】

    题的意思是说进行一次变化操作之后,变成新的序列,然后序列又变回原状,然后再进行下一个变换,变成新状态->又变回原状。。

    然后在这几次变化之后形成的所有序列。找一个下标a1,a2..at,在这所有形成的序列中都都要满足a[a1]<=a[a2]<=...<=a[at].

    求t的最大值。

    设下标为i的数字最大会变成r[i],最小会变成l[i].然后原数字是v[i]

    则第j个元素能接在第i个元素后面加长以第i个元素为结尾的最长上升序列的长度的条件是r[i] <= a[j]且a[i] <= l[j].前者是第i个元素发生变化,后者是第j个元素发生变化所要满足的情况。

    还是提醒一句。进行一次操作过后。马上序列又变回原状。而且每次操作只会改变一个对应下标的数字。

    则f[j] = max(f[i])+1;(i∈1..j-1;a[i] <= l[j]r[i] <= a[j])

    但是n=10W.

    这种做法是n^2的。。

    于是做一下改进。

    我们在转移的时候实际上就是在找某些范围里面的点。

    我们设点的坐标为(a[p],r[q]);(抽象)

    那我们对第i个数字进行DP的时候

    实际上就是要在某种数据结构中找坐标范围为(0..l[i],0..a[i])的点里面f值最大的。

    转移完之后就获得了f[i]的值。

    然后再把第i个点的f[i]加到这种数据结构里面就好了。

    这种问题可以用kdtree来实现。

    n太巨大。需要不时重建kdtree。不然插入操作会让kdtree退化。

    经过试验每6000次重建一次效果最好。5000也能通过。

    4000、10000这些周期则会T

    具体看代码

    【代码】

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int MAXN = 109000;
    
    int n, m,root,ans = 0,now;
    
    struct point
    {
    	int l, r,v;
    };
    
    struct node
    {
    	int ma_x[2], mi_n[2], l, r, dot,f, max,d[2];
    };
    
    point shuju[MAXN];
    node t[MAXN],op;
    int totn = 0,mi_n[2],ma_x[2];
    
    void input_data()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; i++)
    	{
    		scanf("%d", &shuju[i].v);
    		shuju[i].l = shuju[i].r = shuju[i].v;
    	}
    	for (int i = 1; i <= m; i++)
    	{
    		int index, num;
    		scanf("%d%d", &index, &num);
    		shuju[index].l = min(shuju[index].l, num);
    		shuju[index].r = max(shuju[index].r, num);
    	}
    }
    
    void up_data(int rt) 
    {
    	int l = t[rt].l, r = t[rt].r;
    	for (int i = 0; i <= 1; i++)
    	{
    		if (l)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[l].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[l].mi_n[i]);
    			if (t[l].max > t[rt].max)
    			{
    				t[rt].max = t[l].max;
    				//t[rt].dot = t[l].dot;
    			}
    		}
    		if (r)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[r].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[r].mi_n[i]);
    			if (t[r].max > t[rt].max)
    			{
    				t[rt].max = t[r].max;
    				//t[rt].dot = t[r].dot;
    			}
    		}
    	}
    }
    
    void insert(int &rt, int fx)//插入节点
    {
    	if (!rt)
    	{
    		rt = ++totn;
    		t[rt] = op;
    		t[rt].max = t[rt].f;
    		t[rt].l = t[rt].r = 0;
    		//t[rt].dot = rt;
    		for (int i = 0; i <= 1; i++)
    			t[rt].ma_x[i] = t[rt].mi_n[i] = t[rt].d[i];
    		return;
    	}
    	else
    	{
    		if (op.d[fx] < t[rt].d[fx])
    			insert(t[rt].l, 1 - fx);
    		else
    			insert(t[rt].r, 1 - fx);
    	}
    	up_data(rt);
    }
    
    int query(int rt)
    {
    	if (!rt)
    		return 0;
    	if (mi_n[0] <= t[rt].mi_n[0] && t[rt].ma_x[0] <= ma_x[0]
    		&& mi_n[1] <= t[rt].mi_n[1] && t[rt].ma_x[1] <= ma_x[1])
    		return t[rt].max; //整棵子树里面的点都在所求范围内
    	if (t[rt].ma_x[0] < mi_n[0] || t[rt].mi_n[0] > ma_x[0] || t[rt].ma_x[1] < mi_n[1]
    		|| t[rt].mi_n[1] > ma_x[1])//都不在范围内
    		return 0;
    	int big = 0;
    	if (mi_n[0] <= t[rt].d[0] && t[rt].d[0] <= ma_x[0] &&
    		mi_n[1] <= t[rt].d[1] && t[rt].d[1] <= ma_x[1])
    			big = max(big, t[rt].f);//当前这个节点在范围内。
    	int l = t[rt].l, r = t[rt].r;
    	if (l)
    		big = max(big, query(l));
    	if (r)
    		big = max(big, query(r));
    	return big;
    }
    
    bool cmp(node a, node b)
    {
    	return a.d[now] < b.d[now];
    }
    
    int rebuild(int begin, int end, int fx)
    {
    	int m = (begin + end) >> 1;
    	now = fx;
    	nth_element(t + begin, t + m, t + end + 1, cmp);
    	for (int i = 0; i <= 1; i++)
    		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
    	t[m].max = t[m].f;
    	if (begin < m)
    		t[m].l = rebuild(begin, m - 1, 1 - fx);
    	else
    		t[m].l = 0;//不能省,要重建的
    	if (m < end)
    		t[m].r = rebuild(m + 1, end, 1 - fx);
    	else
    		t[m].r = 0;
    	up_data(m);
    	return m;
    }
    
    void get_ans()
    {
    	//j < i r[j] <= v[i]
    	//j < i v[j] <= l[i]
    	op.d[0] = shuju[1].r;
    	op.d[1] = shuju[1].v;
    	op.f = 1;
    	insert(root,0);
    	ans = 1;
    	for (int i = 2;i <= n;i++)
    	{
    		if ((i % 6000) == 0)
    		{
    			root = rebuild(1, totn,0);
    		}
    		mi_n[0] = 0; ma_x[0] = shuju[i].v;
    		mi_n[1] = 0; ma_x[1] = shuju[i].l;
    		int temp = query(root);
    		op.d[0] = shuju[i].r;
    		op.d[1] = shuju[i].v;
    		op.f = 1;
    		if (temp)
    		{
    			if (temp + 1 > ans)
    				ans = temp + 1;
    			op.f = temp + 1;
    		}
    		insert(root, 0);
    	}
    }
    
    void output_ans()
    {
    	printf("%d
    ", ans);
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;
    }


  • 相关阅读:
    Wide & Deep Learning for Recommender Systems
    两个经典问题
    Vlog简介
    中文dumps显示
    用Python提取中文关键词
    【STL】算法 — partial_sort
    c字符串分割 strtok()
    JSP的声明(statement)
    layui之ajax巨坑
    jQuery 库中的 $() 是什么?(答案如下)
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632233.html
Copyright © 2011-2022 走看看