zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:队长快跑(DP+离散化+线段树)

    题目背景

    传说中,在远古时代,巨龙大$Y$将$P$国的镇国之宝窃走并藏在了其巢穴中,
    这吸引着整个$P$国的所有冒险家前去夺回,尤其是皇家卫士队的队长小$W$。
    在$P$国量子科技实验室的帮助下,队长小$W$通过量子传输进入了巨龙大$Y$的藏宝室,并成功夺回了镇国之宝。
    但此时巨龙布下的攻击性防壁启动,将小$W$困在了美杜莎的迷宫当中。


    题目传送门(内部题41)


    输入格式

    第一行一个数$n$,表示水晶的个数。
    接下来的$n$行,每行两个数$A_i,B_i$,表示第$i$个位置上的水晶的属性值。


    输出格式

    输出只有一行,表示在避免机关爆炸的情况下小$W$最多能够摧毁的水晶个数。


    样例

    样例输入:

    5
    2 5
    3 3
    7 2
    8 3
    4 5

    样例输出:

    3


    数据范围与提示

    对于$30\%$的数据,满足$nleqslant 10$。
    对于$60\%$的数据,满足$nleqslant 200$。
    对于另$10\%$的数据,满足$A_i,B_ileqslant 2$。
    对于$100\%$的数据,满足$nleqslant 100,000,1leqslant A_i,B_ileqslant {10}^9$。


    题解

    爆搜有$30$分,记忆化能跑到$60$,再加上特判$A_i,B_ileqslant 2$的点,$70$分就到手了,出题人还是蛮仁慈的。

    首先,$A_i,B_i$肯定要离散化。

    我们要摧毁的肯定是一个连续的区间,所以对于这段合法区间,满足$B_max<A_min$。

    那么考虑$DP$。

    设$dp[i][j]$表示处理到第$i$个水晶,已经选择的水晶中最小的$A$为$S$的最大摧毁个数。

    那么考虑如何转移,分两种情况:

      $alpha.A_ileqslant B_i$:$dp[i][A_i]=max(dp[i-1][B_i+1],dp[i-1][B_i+2]...dp[i-1][MAX])+1$。

      $eta.A_i>B_i$:

        $dp[i][A_i]=max(dp[i-1][A_i+1],dp[i-1][A_i+2]...dp[i-1][MAX]+1$。

        $dp[i][j]=dp[i-1][j]+1,jin(B_i,A_i]$。

    这时候的时间复杂度是$Theta(n^3)$的,不过我们可以用线段树优化$DP$的第二维。

    时间复杂度:$Theta(nlog n)$。

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    int n;
    int a[300000],b[300000];
    pair<int,int> s[300000];
    int tr[1000000],lz[1000000];
    int ans,cnt,now;
    void pushup(int x){tr[x]=max(tr[L(x)],tr[R(x)]);}
    void pushdown(int x)
    {
    	if(!lz[x])return;
    	tr[L(x)]+=lz[x];
    	tr[R(x)]+=lz[x];
    	lz[L(x)]+=lz[x];
    	lz[R(x)]+=lz[x];
    	lz[x]=0;
    }
    int ask(int x,int l,int r,int L,int R)
    {
    	if(R<l||r<L)return 0;
    	if(L<=l&&r<=R)return tr[x];
    	pushdown(x);
    	int mid=(l+r)>>1;
    	return max(ask(L(x),l,mid,L,R),ask(R(x),mid+1,r,L,R));
    }
    void add(int x,int l,int r,int k,int w)
    {
    	if(l==r)
    	{
    		tr[x]=max(tr[x],w);
    		return;
    	}
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(k<=mid)add(L(x),l,mid,k,w);
    	else add(R(x),mid+1,r,k,w);
    	pushup(x);
    }
    void change(int x,int l,int r,int L,int R)
    {
    	if(R<l||r<L)return;
    	if(L<=l&&r<=R)
    	{
    		tr[x]++;
    		lz[x]++;
    		return;
    	}
    	pushdown(x);
    	int mid=(l+r)>>1;
    	change(L(x),l,mid,L,R);
    	change(R(x),mid+1,r,L,R);
    	pushup(x);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&a[i],&b[i]);
    		s[++cnt]=make_pair(a[i],cnt);
    		s[++cnt]=make_pair(b[i],cnt);
    	}
    	sort(s+1,s+cnt+1);
    	s[0].first=-1;
    	for(int i=1;i<=cnt;i++)
    	{
    		if(s[i].first!=s[i-1].first)now++;
    		if(s[i].second&1)a[(s[i].second+1)>>1]=now;
    		else b[s[i].second>>1]=now;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		int maxn=((b[i]^now)?ask(1,1,now,b[i]+1,now):0)+1;
    		ans=max(ans,maxn);
    		if(a[i]<=b[i]+1)add(1,1,now,a[i],maxn);
    		else
    		{
    			maxn=ask(1,1,now,a[i],now)+1;
    			add(1,1,now,a[i],maxn);
    			change(1,1,now,b[i]+1,a[i]-1);
    		}
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    js数组方法大全
    自定义函数实现10进制转化为16进制
    函数的递归
    自定义函数实现atoi功能
    自定义函数实现英文字母大小写的转化
    exit函数和return语句
    函数的返回值
    函数的形参与实参
    函数的定义与声明
    字符串的高级应用-char a[100] = "1+2=;3-2=;2*5=;8/4=;" 得到char a[100] ="1+2=3;3-2=1;2*5=10;8/4=2;"
  • 原文地址:https://www.cnblogs.com/wzc521/p/11524121.html
Copyright © 2011-2022 走看看