连续看几天电影 最多能获得的值【线段树模板】

去年秋天做的题目,大意是:小镇会连续放n天电影,每天都会放一部编号为x[i]的电影,观看后能获得的对应的值是y[x[i]]。有一个人只能挑连续的几天看电影,并且相同编号的电影他看了超过一遍就会厌倦,这时就会“恢复”成“他没有看过这部电影”的状态(即把这部电影获得的值取消了),如果第一次看就会加上对应的值。问最多能获得多少值?

并且这题无法用树状数组(不能区间更新区间求值)http://opentrains.snarknews.info/~ejudge/team.cgi?SID=5455d7e1154ad0d3&action=2

#include<bits/stdc++.h>
#define MAXM 2000010
using namespace std;
int a[MAXM];
long long v[MAXM];
long long x[MAXM],y[MAXM];
int n, m;
long long ans = 0;
vector<int> q[MAXM];
long long sum[MAXM];
int line[MAXM];
struct SegTree //求最值
{
	struct node
	{
		long long num, sum;
	}tree[MAXM << 2];
	int L, R;
	long long V;
	void pushDown(int u, int l, int r, int m)//向下更新节点(节省时间)
	{
		if (tree[u].num != 0)
		{
			node &lson = tree[u << 1], &rson = tree[u << 1 | 1];
			lson.num += tree[u].num;
			rson.num += tree[u].num;
			tree[u].sum += tree[u].num;
			tree[u].num = 0;
		}
	}

	void pushUp(int u) //【改】 
	{   
		tree[u].sum = max(tree[u << 1].sum + tree[u << 1].num, tree[u << 1 | 1].sum + tree[u << 1 | 1].num);
	}
	void built(int u, int l, int r)
	{
		tree[u].num = 0;
		if (l == r)
		{
			tree[u].sum = 0;
			return;
		}
		int mid = l + r >> 1;
		built(u << 1, l, mid);
		built(u << 1 | 1, mid + 1, r);
		pushUp(u);
	}

	void _add(int u, int l, int r)//区间或者点加值
	{
		int mid = l + r >> 1;
		if (L <= l&&r <= R)
		{
			tree[u].num += V; //【改,求和是V*(r-l+1)】 
			pushDown(u, l, r, mid);
			return;
		}
		pushDown(u, l, r, mid);
		if (L <= mid)
			_add(u << 1, l, mid);
		if (R>mid)
			_add(u << 1 | 1, mid + 1, r);
		pushUp(u);
	}

	long long _query(int u, int l, int r)
	{
		int mid = l + r >> 1;
		if (L <= l&&r <= R)
		{
			pushDown(u, l, r, mid);
			return tree[u].sum;
		}
		pushDown(u, l, r, mid);
		long long cnt = 0;
		if (L <= mid)
			cnt = max(cnt, _query(u << 1, l, mid)); //【改】 
		if (mid<R) 
			cnt = max(cnt, _query(u << 1 | 1, mid + 1, r)); //【改】 
		pushUp(u);
		return cnt;
	}

	void add(int l, int r, long long v)
	{
		L = l;
		R = r;
		V = v;
		_add(1, 1, n);
	}
	long long query(int l, int r)
	{
		if (l <= r)
		{
			L = l, R = r;
			return _query(1, 1, n);
		}
		else
			return 0;
	}
}A;
int w[1000005];
int fa[1000005];
int main(){      
    cin>>n>>m;
    for(int i=1;i<=n;++i){  
        cin>>x[i];  
        if (w[x[i]]>=1)  
            fa[i]=w[x[i]];  
        w[x[i]]=i;  
    }  
    for(int i=1;i<=m;++i)
    	cin>>y[i];
    long long maxx=0;
    for(int i=1;i<=n;++i){
    	A.add(fa[i]+1,i,y[x[i]]);
    	if(fa[i]!=0)
    		A.add(fa[fa[i]]+1,fa[i],-y[x[i]]); //fa[fa[i]]+1比fa[i]大就会报错 
    	maxx=max(maxx,A.query(1,i));
//		update(fa[i],-y[x[i]]);
//		update(fa[i],-y[x[i]]);
//		update(fa[fa[i]],y[x[i]]);
//      【树状数组要实现区间更新的话,就实现不了区间求和 】 
//    	cout<<"// "<<sum(2)<<" "<<sum(3)<<" "<<sum(4)<<" "<<sum(5)<<" "<<sum(6)<<" "<<sum(7)<<" "<<sum(8)<<" "<<sum(9)<<" "<<sum(10)<<end;
    }
   cout<<maxx<<endl; 
    return 0;      
}   
#include<bits/stdc++.h>
#define MAXM 2000010
using namespace std;
int a[MAXM];
long long v[MAXM];
long long x[MAXM],y[MAXM];
int n, m;
long long ans = 0;
vector<int> q[MAXM];
long long sum[MAXM];
int line[MAXM];
struct SegTree //求区间和  
{
	struct node
	{
		long long num, sum;
	}tree[MAXM << 2];
	int L, R;
	long long V;
	void pushDown(int u, int l, int r, int m)//向下更新节点(节省时间)
	{
		if (tree[u].num != 0)
		{
			node &lson = tree[u << 1], &rson = tree[u << 1 | 1];
			lson.num += tree[u].num;
			rson.num += tree[u].num;
			tree[u].sum += tree[u].num;
			tree[u].num = 0;
		}
	}

	void pushUp(int u) //【改】 
	{
		tree[u].sum = tree[u << 1].sum + tree[u << 1].num + tree[u << 1 | 1].sum + tree[u << 1 | 1].num;
	}
	void built(int u, int l, int r)
	{
		tree[u].num = 0;
		if (l == r)
		{
			tree[u].sum = 0;
			return;
		}
		int mid = l + r >> 1;
		built(u << 1, l, mid);
		built(u << 1 | 1, mid + 1, r);
		pushUp(u);
	}

	void _add(int u, int l, int r)//区间或者点加值
	{
		int mid = l + r >> 1;
		if (L <= l&&r <= R)
		{
			tree[u].num += V*(r-l+1); //【改,求最值是V】 
			pushDown(u, l, r, mid);
			return;
		}
		pushDown(u, l, r, mid);
		if (L <= mid)
			_add(u << 1, l, mid);
		if (R>mid)
			_add(u << 1 | 1, mid + 1, r);
		pushUp(u);
	}

	long long _query(int u, int l, int r)
	{
		int mid = l + r >> 1;
		if (L <= l&&r <= R)
		{
			pushDown(u, l, r, mid);
			return tree[u].sum;
		}
		pushDown(u, l, r, mid);
		long long cnt = 0;
		if (L <= mid)
			cnt +=  _query(u << 1, l, mid); //【改】 
		if (mid<R)
			cnt += _query(u << 1 | 1, mid + 1, r); //【改】 
		pushUp(u);
		return cnt;
	}

	void add(int l, int r, long long v)
	{
		L = l;
		R = r;
		V = v;
		_add(1, 1, n);
	}
	long long query(int l, int r)
	{
		if (l <= r)
		{
			L = l, R = r;
			return _query(1, 1, n);
		}
		else
			return 0;
	}
}A;
int main(){      
    cin>>n>>m;
    for(int i=1;i<=n;++i){  
        cin>>x[i];  
        A.add(i,i,x[i]);  
    }  
    for(int i=1;i<=n;++i){
    	for(int j=i;j<=n;++j){
    		cout<<i<<" "<<j<<"   "<<A.query(i,j)<<endl; 
    	}
    }
    return 0; 
} 
/* 改编题目,求各区间和 
9 4
2 3 1 1 4 1 2 4 1
*/

CF380C(div.1)线段树高级推理题

//题目大意:给出一串括号,然后m次询问,问说a,b之间有多少个括号匹配。
//解题思路:首先遍历一遍,将每个位置的从1到当前位置的有效右括号数和没有用到的左括号数记录下来;
//然后每次查询a,b区间,即为t = r[b] - r[a-1](有效右括号数),
//但是要注意,这些有效右括号的匹配左括号可能不在区间上,所以要减去l[a-1](未用到的左括号数),
//但是又有可能[1,a-1]中未用到的左括号和[b+1,len]中的右括号相匹配,所以要加上min(l[a~b]),用到线段树查询最小值。
#include<bits/stdc++.h>  
#define MAXM 2000010  
using namespace std;  
int a[MAXM];  
long long v[MAXM];  
long long x[MAXM],y[MAXM];  
int n, m;  
long long ans = 0;  
vector<int> q[MAXM];  
long long sum[MAXM];  
int line[MAXM];  
struct SegTree //求最值  
{  
    struct node  
    {  
        long long num, sum;  
    }tree[MAXM << 2];  
    int L, R;  
    long long V;  
    void pushDown(int u, int l, int r, int m)//向下更新节点(节省时间)  
    {  
        if (tree[u].num != 0)  
        {  
            node &lson = tree[u << 1], &rson = tree[u << 1 | 1];  
            lson.num += tree[u].num;  
            rson.num += tree[u].num;  
            tree[u].sum += tree[u].num;  
            tree[u].num = 0;  
        }  
    }  
  
    void pushUp(int u) //【改】   
    {     
        tree[u].sum = min(tree[u << 1].sum + tree[u << 1].num, tree[u << 1 | 1].sum + tree[u << 1 | 1].num);  
    }  
    void built(int u, int l, int r)  
    {  
        tree[u].num = 0;  
        if (l == r)  
        {  
            tree[u].sum = 0;  
            return;  
        }  
        int mid = l + r >> 1;  
        built(u << 1, l, mid);  
        built(u << 1 | 1, mid + 1, r);  
        pushUp(u);  
    }  
  
    void _add(int u, int l, int r)//区间或者点加值  
    {  
        int mid = l + r >> 1;  
        if (L <= l&&r <= R)  
        {  
            tree[u].num += V; //【改,求和是V*(r-l+1)】   
            pushDown(u, l, r, mid);  
            return;  
        }  
        pushDown(u, l, r, mid);  
        if (L <= mid)  
            _add(u << 1, l, mid);  
        if (R>mid)  
            _add(u << 1 | 1, mid + 1, r);  
        pushUp(u);  
    }  
  
    long long _query(int u, int l, int r)  
    {  
        int mid = l + r >> 1;  
        if (L <= l&&r <= R)  
        {  
            pushDown(u, l, r, mid);  
            return tree[u].sum;  
        }  
        pushDown(u, l, r, mid);  
        long long cnt = 100000000;  //注意 
        if (L <= mid)  
            cnt = min(cnt, _query(u << 1, l, mid)); //【改】   
        if (mid<R)   
            cnt = min(cnt, _query(u << 1 | 1, mid + 1, r)); //【改】   
        pushUp(u);  
        return cnt;  
    }  
  
    void add(int l, int r, long long v)  
    {  
        L = l;  
        R = r;  
        V = v;  
        _add(1, 1, n);  
    }  
    long long query(int l, int r)  
    {  
        if (l <= r)  
        {  
            L = l, R = r;  
            return _query(1, 1, n);  
        }  
        else  
            return 0;  
    }  
}A;  
int l[1000005];
int r[1000005];
int main(){        
    char x[1000005];
    scanf("%s",x);
    n=strlen(x);
    int a=0,b=0,s=0;
    for(int i=1;i<=n;++i){
    	if(x[i-1]=='(')
    		a++;
    	else
    		b++;
    	b=min(a,b); 
    	if(a>0&&b>0){
    		s++;
    		a--;
    		b--;
    	}
    	l[i]=a;
    	r[i]=s;
    	A.add(i,i,a);
    }
    int q;
	scanf("%d",&q);
	while(q--){
		scanf("%d%d",&a,&b);
		printf("%d\n",((r[b]-r[a-1])-max(l[a-1]-(int)A.query(a,b),0))*2);
	} 
    return 0;        
}     


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值