Easy DP Problem

https://codeforces.com/gym/102770/problem/E

给一个dp转移式子,求dp[m][k] m=r-l+1

dp不是玄学吗?

话说给我了一个式子,我直接转不就好了,发现n<1e5,那算了

分析一个小例子发现dp[m][k]=\sum_{i=1}^{m} i^{2}+\sum_{val=No.1}^{No.k} val

前面式子用循环求或者公式,后面维护区间前k之和即可

主席树板子

记得io优化,开ll,还有多组样例,主席树清空

// Problem: E. Easy DP Problem
// Contest: Codeforces - The 17th Zhejiang Provincial Collegiate Programming Contest
// URL: https://codeforces.com/gym/102770/problem/E
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
#include<algorithm>
#include<vector>
#define INF 1e6
using namespace std;
typedef long long ll;
const int N=1e5+9;
int a[N];
vector<int> X;
//主席树
ll binary(int x){
	return lower_bound(X.begin(),X.end(),x)-X.begin()+1;
}
struct ZXSEG{
	struct node{
	    int l,r;//左右儿子,关系
	    int s;//节点值域中有多少个数
	    ll sum;
	    ll val;
	}seg[N*22];//n*(logn*3);logn+3向上取整
	#define tl(id) seg[id].l//宏定义简洁
	#define tr(id) seg[id].r
	int root[N],index;//根节点,节点数量
	void reset(int n){
		for(int i=1;i<=n;i++){
			root[i]=0;
		}
		index=0;
	}
	void build(int &id,int l,int r){//传引用,通过子空间id值给父空间的gx[0],gx[1]赋值
	    id=++index;
	    if(l==r){
	        return;
	    }
	    int mid=(l+r)>>1;
	    build(tl(id),l,mid);
	    build(tr(id),mid+1,r);
	}
	/*
	递归建立各个历史版本的线段树
	post是前一个版本的节点指针,curr是当前版本的节点指针
	curr是传引用,通过子空间curr值,给父空间的tl(curr),tr(curr)赋值
	*/
	void insert(int post,int &curr,int l,int r,int v){
	    curr=++index;//开新节点
	    seg[curr]=seg[post];//赋值
	    seg[curr].s++;//更新
	    seg[curr].sum+=X[v-1];
	    if(l==r){
	    	seg[curr].val=X[v-1];
	    	return;
	    }
	    int mid=(l+r)>>1;
	    if(v<=mid){//在左边递归
	        insert(tl(post),tl(curr),l,mid,v);
	    }else{
	        insert(tr(post),tr(curr),mid+1,r,v);
	    }
	}
	/*
	找区间[l,r]内的第k小,插入r的历史版本,进行二分
	[l,r]==[1,r]-[1,l-1];
	*/
	ll query(int post,int curr,int l,int r,ll k){
	    if(l==r){//
	        return seg[curr].val*k;
	    }
	    int mid=(l+r)>>1;
	    int s=seg[tr(curr)].s-seg[tr(post)].s;//变化信息
	    if(k<=s){//找k的位置
	        return query(tr(post),tr(curr),mid+1,r,k);
	    }else{
	        return seg[tr(curr)].sum-seg[tr(post)].sum+query(tl(post),tl(curr),l,mid,k-s);//减去左边的
	    }
	}
}t;
void solve(){
	int n;
	cin>>n;
	X.clear();
	t.reset(n);
	for(int i=1;i<=n;i++){
		cin>>a[i];
		X.push_back(a[i]);
	}
	sort(X.begin(),X.end());
	X.erase(unique(X.begin(),X.end()),X.end());
	int xn=X.size();
	t.build(t.root[0],1,xn);
	for(int i=1;i<=n;i++){
		t.insert(t.root[i-1],t.root[i],1,xn,binary(a[i]));
	}
	int q;
	cin>>q;
	for(int i=1;i<=q;i++){
		int l,r,k;
		cin>>l>>r>>k;
		ll m=(r-l+1);
		ll t1=m*(m+1ll)*(2ll*m+1ll);
		t1/=6ll;
		ll t2=t.query(t.root[l-1],t.root[r],1,xn,k);
		ll ans=t1+t2;
		cout<<ans<<'\n';
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值