• AT1757 花火 题解


    这是我根据官方题解复盘出来的做法。

    (O(n^2))DP显然。

    (dp[i][j])表示i时刻在位置j的最小代价。

    转移:

    [ dp[i][j]=min_{k<=j}(dp[i-1][k])+ ( i 时间内在位置 j 所有烟花的不满值之和) ]

    (dp'[i][j]=min_{k<=j}(dp[i][k])) (就是做了一个前缀min)

    (dp[i][j]=dp'[i-1][j]+) ( (i) 时间内在位置 (j) 所有烟花的不满值之和)

    (dp'[i][j]=min(dp'[i][j-1],dp[i][j]))

    从“ (i) 时间内在位置 (j) 所有烟花的不满值之和”入手,令(f(i))表示在 (i) 位置,某一烟花造成的不满值。我们可以发现, $f(i)=|x_i-i| $( (x_i) 表示烟花绽放的位置) 。

    显然,(f(i))是一个下凸函数,那么对于在同一时刻绽放的所有烟花,(f(i))的和也为下凸函数。(若干个下凸函数的和仍然是下凸函数)

    考虑我们的 (dp) 过程,是先加了同一时刻绽放的所有烟花的不满值的和,然后做了一遍前缀min,然后重复。

    最开始dp函数是一条 (y=0) 的直线,加上 同一时刻绽放的所有烟花的不满值的和(一个下凸函数)之后仍是一个下凸函数,然后做一遍前缀min,仍然是一个下凸函数,而且对于下凸函数取min就是把后面一段导数大于0的一段的导数改为0。

    为了方便维护下凸函数,官方题解采取了维护差分序列的方法,下面是我根据官方题解写的代码,线段树区间覆盖部分蒯的是这篇博客

    #include<bits/stdc++.h>
    using namespace std;
    const long long maxn = 100007,INF=214748364700000;
    long long Max[maxn << 2], changetag[maxn << 2], addtag[maxn << 2];
    long long a[maxn],t[maxn],m,ans(INF),dp[maxn];
    long long n, LL;
     
    inline void PushUp(long long rt) {
    	Max[rt] = max(Max[rt<<1], Max[rt<<1|1]);
    }
     
    inline void PushDown(long long rt) {
    	if (changetag[rt] != -INF) {
    		Max[rt<<1] = changetag[rt];
    		Max[rt<<1|1] = changetag[rt];
    		changetag[rt << 1] = changetag[rt];
    		addtag[rt << 1] = 0;
    		changetag[rt << 1 | 1] = changetag[rt];
    		addtag[rt << 1 | 1] = 0;
    		changetag[rt] = -INF;
    	} else if (addtag[rt]) {
    		Max[rt<<1] += addtag[rt];
    		Max[rt<<1|1] += addtag[rt];
    		if (changetag[rt<<1] != -INF) changetag[rt<<1] += addtag[rt];
    		else addtag[rt<<1] += addtag[rt];
    		if (changetag[rt<<1|1] != -INF) changetag[rt<<1|1] += addtag[rt];
    		else addtag[rt<<1|1] += addtag[rt];
    		addtag[rt] = 0;
    	}
    }
     
    void build(long long rt, long long l, long long r) {
    	changetag[rt] = -INF;
    	addtag[rt] = 0;
    	if (l == r) {
    		Max[rt] = 0;
    		return;
    	}
    	long long m = (l + r) >> 1;
    	build(rt<<1, l, m);
    	build(rt<<1|1, m+1, r);
    //	PushUp(rt);
    }
     
    void change(long long rt, long long l, long long r, long long L, long long R, long long C) {
    	if (L <= l && r <= R) {
    		Max[rt] = C;
    		changetag[rt] = C;
    		addtag[rt] = 0;
    		return;
    	}
    	long long m = (l + r) >> 1;
    	PushDown(rt);
    	if (L <= m) change(rt<<1, l, m, L, R, C);
    	if (R > m) change(rt<<1|1, m+1, r, L, R, C);
    	PushUp(rt);
    }
     
    void add(long long rt, long long l, long long r, long long L, long long R, long long C) {
    	if (L <= l && r <= R) {
    		Max[rt] = Max[rt] + C;
    		if (changetag[rt] == -INF) addtag[rt] += C;
    		else changetag[rt] += C;
    		return;
    	}
    	long long m = (l + r) >> 1;
    	PushDown(rt);
    	if (L <= m) add(rt<<1, l, m, L, R, C);
    	if (R > m) add(rt<<1|1, m+1, r, L, R, C);
    	PushUp(rt);
    }
     
    long long query(long long rt, long long l, long long r, long long L, long long R) {
    	if (L <= l && r <= R) return Max[rt];
    	long long m = (l + r) >> 1, res = -INF;
    	PushDown(rt);
    	if (L <= m) res = max(res, query(rt<<1, l, m, L, R));
    	if (R > m) res = max(res, query(rt<<1|1, m+1, r, L, R));
    	return res;
    }
    long long read() {
    	long long x(0);
    	char ch(getchar());
    	while(ch>'9'||ch<'0')ch=getchar();
    	while(ch<='9'&&ch>='0') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return x;
    }
    int main() {
    //	freopen("testdata.in","r",stdin);
    	n=read();
    	LL=read();
    	for (long long i = 1; i <= n; ++i)
    		scanf("%lld%lld",&t[i],&a[i]);
    	build(1, 1, LL);
    	long long head(1),dps(a[1]);
    	while (head<=n) {
    		if(a[head])add(1,1,LL,1,a[head],-1);
    		if(a[head]!=LL)add(1,1,LL,a[head]+1,LL,1);
    		while(t[head]==t[head+1]) {
    			++head;
    			dps+=a[head];
    			if(a[head])add(1,1,LL,1,a[head],-1);
    			if(a[head]!=LL)add(1,1,LL,a[head]+1,LL,1);
    		}
    		long long l(1),r(LL),mid((l+r)>>1);
    		while(l<r) {
    			if(query(1,1,LL,mid,mid)<0)l=mid+1;
    			else r=mid;
    			mid=(l+r)>>1;
    		}
    //		prlong longf("%d ",r);
    		if(r!=LL)
    			change(1,1,LL,r,LL,0);
    		else if(query(1,1,LL,LL,LL)>0)change(1,1,LL,LL,LL,0);
    //		for(long long i=1; i<=LL; i++)
    //			prlong longf("%d ",query(1,1,LL,i,i));
    //		prlong longf("
    ");
    		++head;
    		dps+=a[head];
    	}
    	dp[0]=dps;
    	for(long long i=1; i<=LL; i++) {
    		dp[i]=dp[i-1]+query(1,1,LL,i,i);
    		ans=min(ans,dp[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    java模式及其应用场景
    redis配置密码 redis常用命令
    Redis可视化工具Redis Desktop Manager使用
    String类和StringBuffer类的区别
    centos下搭建redis集群
    eclipse maven项目中使用tomcat插件部署项目
    什么是反向代理,如何区别反向与正向代理
    数据库连接池的原理
    归并排序
    asio-kcp源码分析
  • 原文地址:https://www.cnblogs.com/thedreammaker/p/13160321.html
Copyright © 2020-2023  润新知