• CF1131G Most Dangerous Shark


    一、题目

    点此看题

    二、解法

    考虑每个点的推倒状态只有向左倒和向右倒,然后我们又要把所有骨牌推倒,所以设计 (dp[i]) 表示推倒前 (i) 个骨牌的最小代价,假设我们会处理 (l[i],r[i]) 表示向左推倒骨牌 (i) 覆盖的左端点,向右推倒骨牌 (i) 覆盖的右端点,转移:

    • (i) 向左推倒:(dp[i]=dp[j]+c[i] l[i]-1leq j<i)

    • (j) 向右推倒:(dp[i]=dp[j-1]+c[j] j<ileq r[j])

    第一种转移其实只需要考虑 (j=l[i]-1) 时的转移即可,因为 ([l[i],i]) 的骨牌 (i) 能推倒就不需要前面去管了。第二种转移看似需要线段树优化,做到 (O(n)) 我们需要观察性质。

    不难发现每个骨牌的覆盖范围要么包含,要么相离,所以可以维护一个栈,每次新加入的点就放在栈顶,每次只需要考虑栈顶得覆盖范围够不够,不够直接弹出,然后维护一个栈的前缀最小值就可以转移了。

    现在的问题是算 (l[i],r[i]),就说 (l[i]) 怎么算吧,还是维护一个栈,首先弹出没有用的点,也就是 (i-h[i]leq tp-h[tp]),并且栈顶对于以后的元素也没有用了。然后我们考虑当前点能不能覆盖到栈顶,如果不能的话就直接用 (i-h[i]+1),如果可以的话就用 (l[tp]),不难发现他是最优的。

    三、总结

    考虑每个点的状态有哪些,然后 (dp),只要涉及了所有状态就可以。

    (dp) 的线性优化可以考虑找性质,然后用单调数据结构维护该性质。

    #include <cstdio>
    #include <vector>
    #include <iostream>
    using namespace std;
    const int M = 10000005;
    const int N = 250005;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,q,h[M],c[M],f[M],l[M],r[M],st[M],mn[M];
    vector<int> a[N],b[N];
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    	{
    		int k=read();
    		for(int j=1;j<=k;j++) a[i].push_back(read());
    		for(int j=1;j<=k;j++) b[i].push_back(read());
    	}
    	q=read();n=0;
    	while(q--)
    	{
    		int id=read(),ml=read();
    		for(int i=0;i<a[id].size();i++)
    			h[++n]=a[id][i],c[n]=b[id][i]*ml;
    	}
    	int qr=0;
    	for(int i=1;i<=n;i++)
    	{
    		while(qr && i-h[i]<=st[qr]-h[st[qr]]) qr--;//useless
    		if(!qr || i-h[i]>=st[qr]) l[i]=max(i-h[i]+1,1ll);
    		else l[i]=l[st[qr]];
    		st[++qr]=i;
    	}
    	qr=0;
    	for(int i=n;i>=1;i--)
    	{
    		while(qr && i+h[i]>=st[qr]+h[st[qr]]) qr--;
    		if(!qr || i+h[i]<=st[qr]) r[i]=min(i+h[i]-1,m);
    		else r[i]=r[st[qr]];
    		st[++qr]=i;
    	}
    	qr=0;mn[0]=1e18;
    	for(int i=1;i<=n;i++)
    	{
    		while(qr && r[st[qr]]==i-1) qr--;//illegal
    		f[i]=f[l[i]-1]+c[i];
    		if(qr) f[i]=min(f[i],mn[qr]);
    		st[++qr]=i;mn[qr]=min(mn[qr-1],f[i-1]+c[i]);
    	}
    	printf("%lld
    ",f[n]);
    }
    
  • 相关阅读:
    Go语言之Go 语言函数
    Go语言之Go 语言循环语句
    Go语言之Go 语言条件语句
    Go语言之Go 语言运算符
    Go语言之GO 语言注释
    Go语言之Go 语言类型别名
    7.19 PDO(php data object-php数据对象)数据库抽象层
    7.15 原生js写ajax
    7.15 文件打开后点击打开下级文件
    6.28 js和php数组去重
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15009805.html
Copyright © 2020-2023  润新知