【2020省选模拟】题解

N o i p Noip Noip模拟?


T1

直接树剖即可
维护各凭本事
我是维护的轻儿子之和,所以细节要多很多
直接维护所有儿子之和似乎好做一些
标记永久化了,但似乎还是跑最慢。。。

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define pb push_back
#define pii pair<int,int>
#define bg begin
#define fi first
#define se second
#define ll long long
cs int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
inline ll readll(){
	char ch=gc();
	ll res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
template<typename tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<typename tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=100005;
int n,q,in[N],fa[N],top[N],idx[N],d[N],dep[N],siz[N],son[N],dfn,val[N];
ll sval[N];
vector<int> e[N];
#define pll pair<ll,ll>
namespace Seg{
	cs int N=::N<<2;
	ll s[N],vtag[N],ss[N],stag[N];
	int sz[N],sd[N];
	#define lc (u<<1)
	#define rc ((u<<1)|1)
	#define mid ((l+r)>>1)
	inline void pushup(int u){
		s[u]=s[lc]+s[rc]+vtag[u]*sz[u];
		ss[u]=ss[lc]+ss[rc]+stag[u]*sd[u];
	}
	void build(int u,int l,int r){
		if(l==r){sz[u]=1,sd[u]=d[idx[l]],s[u]=val[idx[l]],ss[u]=sval[idx[l]];return;}
		build(lc,l,mid),build(rc,mid+1,r);
		sz[u]=sz[lc]+sz[rc],sd[u]=sd[lc]+sd[rc];
		s[u]=s[lc]+s[rc],ss[u]=ss[lc]+ss[rc];
	}
	inline void pushnow(int u,int kv,int ks){
		s[u]+=(ll)kv*sz[u],vtag[u]+=kv;
		ss[u]+=(ll)ks*sd[u],stag[u]+=ks;
	}
	void update(int u,int l,int r,int st,int des,int kv,int ks){
		if(st<=l&&r<=des)return pushnow(u,kv,ks);
		(st<=mid)&&(update(lc,l,mid,st,des,kv,ks),1);
		(mid<des)&&(update(rc,mid+1,r,st,des,kv,ks),1);
		pushup(u);
	}
	pll query(int u,int l,int r,int st,int des){
		if(st>des)return pll(0,0);
		if(st<=l&&r<=des)return pll(s[u]+ss[u],sd[u]);
		ll res=vtag[u]*(min(des,r)-max(st,l)+1),ret=0;pll x;
		if(st<=mid){
			x=query(lc,l,mid,st,des);
			res+=x.fi,ret+=x.se;
		}
		if(mid<des){
			x=query(rc,mid+1,r,st,des);
			res+=x.fi,ret+=x.se;
		}res+=ret*stag[u];
		return pll(res,ret);
	}
	ll querytag(int u,int l,int r,int p){
		if(l==r)return stag[u];
		return (p<=mid)?querytag(lc,l,mid,p)+stag[u]:querytag(rc,mid+1,r,p)+stag[u];
	}
	ll queryval(int u,int l,int r,int p){
		if(l==r)return s[u];
		return (p<=mid)?queryval(lc,l,mid,p)+vtag[u]:queryval(rc,mid+1,r,p)+vtag[u];
	}
	ll queryss(int u,int l,int r,int p,ll t){
		if(l==r)return ss[u]+t*d[idx[l]];
		return (p<=mid)?queryss(lc,l,mid,p,t+stag[u]):queryss(rc,mid+1,r,p,t+stag[u]);
	}
	void updateval(int u,int l,int r,int p,int k){
		s[u]+=k;
		if(l==r)return;
		(p<=mid)?updateval(lc,l,mid,p,k):updateval(rc,mid+1,r,p,k);
	}
	void updatesv(int u,int l,int r,int p,int k){
		ss[u]+=k;
		if(l==r)return;
		(p<=mid)?updatesv(lc,l,mid,p,k):updatesv(rc,mid+1,r,p,k);
	}
	#undef lc
	#undef rc
	#undef mid
}
inline ll VAL(int x){return Seg::queryval(1,1,n,in[x])+((x==top[x]&&x!=1)?(Seg::querytag(1,1,n,in[fa[x]])):0);}
void pupdate(int u,int v,int k){
	int preu=0,prev=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v),swap(preu,prev);
		Seg::update(1,1,n,in[top[u]],in[u],k,k);
		if(preu)Seg::updateval(1,1,n,in[preu],-k);
		if(son[u])Seg::updateval(1,1,n,in[son[u]],k);
		preu=top[u],u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v),swap(preu,prev);
	Seg::update(1,1,n,in[v],in[u],k,k);
	if(preu)Seg::updateval(1,1,n,in[preu],-k);
	if(son[u])Seg::updateval(1,1,n,in[son[u]],k);
	if(prev)Seg::updateval(1,1,n,in[prev],-k);
	if(fa[v]){Seg::updateval(1,1,n,in[fa[v]],k);
	if(fa[fa[v]]&&top[fa[v]]==fa[v])Seg::updatesv(1,1,n,in[fa[fa[v]]],k);}
	if(v==top[v]&&fa[v])Seg::updatesv(1,1,n,in[fa[v]],k);
}
ll pquery(int u,int v){
	int preu=0,prev=0;ll res=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v),swap(preu,prev);
		res+=Seg::query(1,1,n,in[top[u]]+1,in[u]).fi;
		if(son[u])res+=VAL(son[u]);
		res+=Seg::queryss(1,1,n,in[top[u]],0);
		preu=top[u],u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v),swap(preu,prev);
	res+=Seg::query(1,1,n,in[v],in[u]).fi;
	if(son[u])res+=VAL(son[u]);
	if(fa[v])res+=VAL(fa[v]);
	if(v==top[v]&&fa[v])res+=Seg::querytag(1,1,n,in[fa[v]]);
	return res;
}
void dfs1(int u){
	siz[u]=1,dep[u]=dep[fa[u]]+1;
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==fa[u])continue;
		fa[v]=u,dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs2(int u,int tp){
	in[u]=++dfn,idx[dfn]=u,top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int i=0;i<e[u].size();i++){
		int v=e[u][i];
		if(v==fa[u]||v==son[u])continue;
		sval[u]+=val[v],d[u]++,dfs2(v,v);
	}
}
int main(){
	#ifdef Stargazer
	freopen("tx.in","r",stdin);
	freopen("my.out","w",stdout);
	#endif
	n=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		e[u].pb(v),e[v].pb(u);
	}
	for(int i=1;i<=n;i++)val[i]=read();
	dfs1(1),dfs2(1,1);
	Seg::build(1,1,n);
	q=read();
	while(q--){
		int op=read(),x=read(),y=read();
		if(op==1){
			int c=read();
			pupdate(x,y,c);
		}
		else cout<<pquery(x,y)<<'\n';
	}
}

T2:

考虑从大到小排序后分成很多段,每段打死一个人
每段显然是每个人先打打选权值最小的一个的血量
f [ i ] [ j ] f[i][j] f[i][j]表示最后 i i i个人打死了 j j j
那么 f [ i ] [ j ] = min ⁡ k ∈ ( i , n ] ( a k ( k − i + 1 ) + f [ k + 1 ] [ j − 1 ] ) f[i][j]=\min_{k\in(i,n]}(a_k(k-i+1)+f[k+1][j-1]) f[i][j]=mink(i,n](ak(ki+1)+f[k+1][j1])
发现这个可以斜率优化

每次凸包二分即可,复杂度 O ( T n 2 l o g n ) O(Tn^2logn) O(Tn2logn)

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define pb push_back
#define pii pair<int,int>
#define bg begin
#define fi first
#define se second
#define ll long long
cs int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
inline ll readll(){
	char ch=gc();
	ll res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
template<typename tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<typename tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=2005;
int n,m,a[N],f[N][N];
struct pt{
	int x,y;
	pt(int _x=0,int _y=0):x(_x),y(_y){}
	friend inline pt operator +(cs pt &a,cs pt &b){
		return pt(a.x+b.x,a.y+b.y);
	}
	friend inline pt operator -(cs pt &a,cs pt &b){
		return pt(a.x-b.x,a.y-b.y);
	}
	friend inline ll operator *(cs pt &a,cs pt &b){
		return 1ll*a.x*b.y-1ll*a.y*b.x;
	}
}stk[N];
int top;
inline void push(cs pt &x){
	while(top&&stk[top].x==x.x){
		if(stk[top].y>x.y)top--;
		else return;
	}
	while(top>1&&(stk[top]-stk[top-1])*(x-stk[top-1])<=0)top--;
	stk[++top]=x;
}
inline void solve(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	sort(a+1,a+n+1,greater<int>());
	for(int i=1;i<=m;i++){
		top=0;
		for(int j=n-i+1;j;j--){
			push(pt(a[j],f[i-1][j+1]+a[j]*j));
			int res=1,l=2,r=top;
			while(l<=r){
				int mid=(l+r)/2;
				if((stk[mid]-stk[mid-1])*(pt(1,j-1))>=0)l=mid+1,res=mid;
				else r=mid-1;
			}
			f[i][j]=stk[res].y-stk[res].x*(j-1);
		}
	}
	cout<<f[m][1]<<'\n';
}
int main(){
	int T=read();
	while(T--)solve();
}

T3

之前 c s p csp csp模拟似乎考过一道一模一样的。。。
先不考虑 k k k
考虑对有贡献的关键点 D P DP DP
f [ i ] f[i] f[i]表示当前结尾为 i i i的答案
那么 f [ i ] = max ⁡ j , j < i , a j < a i , a i − i ≤ a j − j f j + 1 f[i]=\max_{j,j<i,a_j<a_i,a_i-i\le a_j-j}f_j+1 f[i]=maxj,j<i,aj<ai,aiiajjfj+1
考虑有三个限制
如果按照 a i − i a_i-i aii排序后,如果 i i i前面有 j j j满足 b i > b j b_i>b_j bi>bj,那么必有 i > j i>j i>j
于是树状数组即可

考虑存在 k k k即答案不可能超过 n − k , a i − i < k n-k,a_i-i<k nk,aii<k判一下即可

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define pb push_back
#define pii pair<int,int>
#define bg begin
#define fi first
#define se second
#define ll long long
cs int RLEN=1<<20|1;
inline char gc(){
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob)?EOF:*ib++;
}
inline int read(){
	char ch=gc();
	int res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
inline ll readll(){
	char ch=gc();
	ll res=0;bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
	return f?res:-res;
}
template<typename tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<typename tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=500005;
int n;
namespace bit{
	int tr[N];
	#define lb(x) (x&(-x))
	void update(int p,int k){
		for(;p<=n;p+=lb(p))chemx(tr[p],k);
	}
	int query(int p,int res=0){
		for(;p>0;p-=lb(p))chemx(res,tr[p]);return res;
	}
}
int k;
pii a[N];
inline bool comp(cs pii &x,cs pii &y){return (x.fi-x.se)==(y.fi-y.se)?(x.se<y.se):(x.fi-x.se>y.fi-y.se);}
int main(){
	n=read(),k=read();int ans=0;
	for(int i=1;i<=n;i++)a[i]=pii(read(),i);
	sort(a+1,a+n+1,comp);
	for(int i=1;i<=n;i++)if(a[i].fi<=a[i].se&&a[i].se-a[i].fi<=k&&a[i].fi<=n-k){
		int now=bit::query(a[i].fi-1)+1;
		bit::update(a[i].fi,now);
		chemx(ans,now);
	}
	cout<<min(ans,n-k)<<'\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值