dp day3 数据结构优化dp(虽然蒟蒻不会)

讲道理今天除了第三题秒杀都难想

N 个红绿灯的直线道路,给出车通过每个路段需要的时间。每个红绿灯都同步(周期相同且红绿灯状态同时改变),其中绿灯 g 秒,红灯 r 秒。有 Q 辆车在某个时刻从起点出发,问分别会在什么时候到达终点。注意红变绿瞬间可以通过,而绿变红瞬间不可通过。

【输入格式】

输入数据第一行包含三个整数N,g,r,表示红绿灯数量,绿灯持续时间,红灯持续时间。接下来一行 N+1 个整数,依次表示从起点到终点的每个路段所需时间。下一行是一个整数 Q,表示询问次数。后面的 Q 行每行一个询问,询问从 t 时刻出发的公交车会在什么时候到达终点。

【输出格式】

对于每个询问输出到达终点的时间。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110003;
struct tr1 {
	tr1* ls;
	tr1* rs;
	int st;
	int lazy;
	tr1() {
		ls = rs = NULL;
		st = lazy = 0;
	}
}*root,dizhi[N*60];
int t;
int n;
long long g,r;
long long T[N];
long long f[N];
inline void push_down(tr1* tr) {
	if(tr->ls==NULL) tr->ls = &dizhi[++t];
	tr->ls->st = tr->lazy;
	if(tr->rs==NULL) tr->rs = &dizhi[++t];
	tr->rs->st = tr->lazy;
	tr->ls->lazy = tr->rs->lazy = tr->lazy;
	tr->lazy = 0;
}
void modify(tr1* tr,int l,int r,int x,int y,int z) {
	if(l!=r && tr->lazy) push_down(tr);
	if(x<=l && r<=y) {
		tr->st = z;
		tr->lazy = z;
		return;
	}
	int mid = (l+r) >> 1;
	if(!(x>mid || y<l)) {
		if(tr->ls==NULL) tr->ls = &dizhi[++t];
		modify(tr->ls,l,mid,x,y,z);
	}
	if(!(x>r||y<mid+1)) {
		if(tr->rs==NULL) tr->rs = &dizhi[++t];
		modify(tr->rs,mid+1,r,x,y,z);
	}
}
int query(tr1* tr,int l,int r,int x) {
	if(tr->lazy) return tr->lazy;
	if(l==r) return tr->st;
	int mid = (l+r) >> 1;
	if(x<=mid) {
		if(tr->ls==NULL) return 0;
		else return query(tr->ls,l,mid,x);
	} else {
		if(tr->rs==NULL) return 0;
		else return query(tr->rs,mid+1,r,x);
	}
}
long long sf[N];
int main() {
	
	freopen("brt.in","r",stdin);
	freopen("brt.out","w",stdout);
	scanf("%d%I64d%I64d",&n,&g,&r);
	for(int i = 1 ; i <= n+1 ; ++i) scanf("%I64d",&T[i]);
	sf[0] = 0;
	for(int i = 1 ; i <= n + 1 ; ++i) sf[i] = sf[i-1] + T[i];
	f[n] = T[n+1];
	root = &dizhi[++t];
	for(int i = n-1 ; i >= 0 ; --i) {
		int tmp = sf[i+1]%(g+r);
		if(tmp<=g) modify(root,0,g+r-1,g-tmp,g+r-tmp-1,i+1);
		else {
			modify(root,0,g+r-1,0,g+r-tmp-1,i+1);
			modify(root,0,g+r-1,g+r-tmp+g,g+r-1,i+1);
		}
		int arrval_time = (g+r-sf[i]%(g+r))%(g+r);
		int tmp2 = query(root,0,g+r-1,arrval_time);
		if(tmp2==0) f[i] = sf[n+1]-sf[i];
		else {
			long long dlt = sf[tmp2]-sf[i];
			f[i] = dlt+g+r-dlt%(g+r)+f[tmp2];
		}
	}
	int Q;
	scanf("%d",&Q);
	while(Q--) {
		long long x,ans;
		scanf("%I64d",&x);
		int tmp2 = query(root,0,g+r-1,x%(g+r));
		if(tmp2==0) ans = sf[n+1]+x;
		else {
			long long dlt = sf[tmp2];
			ans = x+dlt+g+r-(x+dlt)%(g+r)+f[tmp2];
		}
		printf("%I64d\n",ans);
	}
}

给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0

请找到最长的一段连续区间,使得该区间内所有数字之和不超过p

单调队列优化

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int maxn=2*1e6+5;

long long a[maxn],sum[maxn],que[maxn];

long long n,p,d;

int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%I64d%I64d%I64d",&n,&p,&d);
	for (int i=1;i<=n;i++)
	{
		scanf("%I64d",&a[i]);
	    sum[i]=sum[i-1]+a[i];
	}
	long long ans=d;
	int lf=0;
	int head=1;
	int tail=0;
	for (int rg=d;rg<=n;rg++)
	{
		while (head<=tail&&sum[rg]-sum[rg-d]>=sum[que[tail]]-sum[que[tail]-d])
		   tail--;
		que[++tail]=rg;
		while (sum[rg]-sum[lf]-(sum[que[head]]-sum[que[head]-d])>p)
		{
			lf++;
			if (que[head]-d<lf)
			   head++;
		}
		if (rg-lf>ans)
		   ans=rg-lf;
	}
	printf("%I64d\n",ans);
	return 0;
}

题目描述:

           给定一个n*m的矩阵,求最大子矩阵和。

输入:

           第一行两个数n,m。

                 接下来n行每行m个数表示矩阵。

输出:

           一行一个数表示最大子矩阵和。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=505;

const int minInf=-1e9;

int jz[maxn][maxn],b[maxn][maxn];

int n,m,ans=minInf;

int main()
{
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	   for (int j=1;j<=m;j++)
	      scanf("%d",&jz[i][j]);
    for (int i=1;i<=n;i++)
       for (int j=1;j<=m;j++)
          b[i][j]=b[i][j-1]+jz[i][j];
    for (int i=1;i<=n;i++)
    {
    	for (int j=i;j<=m;j++)
    	{
    		int num=0;
    		for (int k=1;k<=n;k++)
    		{
    			if (num>0)
    			   num+=(b[k][j]-b[k][i-1]);
    			else
    			   num=b[k][j]-b[k][i-1];
    			if (num>ans)
    			   ans=num;
    		}
    	}
    }
     printf("%d\n",ans);
	 return 0;  
}
维护每一列的前缀和


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值