【校内模拟20200402】

由于题都比较简单,全部写在一起了。

T1

定义树上的毛毛虫是一条链和所有和这条链相邻的点的集合。

给一棵树,请你维护。修改为选择一个毛毛虫,给所有点加上某个权值。

询问为给一个毛毛虫,求点权值之和。


口胡:

每个点维护所有儿子的权值之和。于是修改变成了链加,最后处理lca和父亲的权值。询问同理。

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;

cs int N=1e5+7;

int n,Q;

int el[N],nx[N<<1|1],to[N<<1|1],ec;
inline void adde(int u,int v){
	nx[++ec]=el[u],el[u]=ec,to[ec]=v;
	nx[++ec]=el[v],el[v]=ec,to[ec]=u;
}

int dfn[N],dfc,ps[N];
int fa[N],d[N],top[N];
int sz[N],son[N],deg[N];

void dfs1(int u,int p){
	fa[u]=p,d[u]=d[p]+1;sz[u]=1;
	for(int re e=el[u];e;e=nx[e])
		if(to[e]!=p){
			dfs1(to[e],u);sz[u]+=sz[to[e]];++deg[u];
			if(sz[to[e]]>sz[son[u]])son[u]=to[e];
		}
}

void dfs2(int u,int tp){
	dfn[u]=++dfc,ps[dfc]=u;top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int re e=el[u];e;e=nx[e])
		if(to[e]!=fa[u]&&to[e]!=son[u])
			dfs2(to[e],to[e]);
}

ll w[N];ll sm[N];

namespace SGT{

cs int N=::N<<2|7;

#define lc u<<1
#define rc u<<1|1

ll tg[N],dsm[N],sm[N];
void build(int u,int l,int r){
	if(l==r){
		sm[u]=::sm[ps[l]];
		dsm[u]=::deg[ps[l]];
		tg[u]=0;return;
	}int mid=(l+r)>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	dsm[u]=dsm[lc]+dsm[rc];sm[u]=sm[lc]+sm[rc];
}

void push_nw(int u,ll w){
	tg[u]+=w;sm[u]+=w*dsm[u];
}

void push_dn(int u){
	if(tg[u]){
		push_nw(lc,tg[u]);
		push_nw(rc,tg[u]);
		tg[u]=0;
	}
}
void add_ch(int u,int l,int r,int ql,int qr,int w){
	if(ql<=l&&r<=qr)return push_nw(u,w);
	int mid=(l+r)>>1;push_dn(u);
	if(ql<=mid)add_ch(lc,l,mid,ql,qr,w);
	if(mid<qr)add_ch(rc,mid+1,r,ql,qr,w);
	sm[u]=sm[lc]+sm[rc];
}
void add_fa(int u,int l,int r,int p,int w){
	if(l==r){sm[u]+=w;return ;}int mid=(l+r)>>1;push_dn(u);
	p<=mid?add_fa(lc,l,mid,p,w):add_fa(rc,mid+1,r,p,w);sm[u]=sm[lc]+sm[rc];
}

ll query(int u,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr)return sm[u];
	int mid=(l+r)>>1;push_dn(u);
	if(qr<=mid)return query(lc,l,mid,ql,qr);
	if(mid<ql)return query(rc,mid+1,r,ql,qr);
	return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}

ll query_tg(int u,int l,int r,int p){
	if(l==r){return tg[u];}int mid=(l+r)>>1;push_dn(u);
	return p<=mid?query_tg(lc,l,mid,p):query_tg(rc,mid+1,r,p);
}

}

void Add(){
	int u=gi(),v=gi(),w=gi();
	while(top[u]!=top[v]){
		if(d[top[u]]<d[top[v]])std::swap(u,v);
		SGT::add_ch(1,1,n,dfn[top[u]],dfn[u],w);u=fa[top[u]];
	}if(d[u]>d[v])std::swap(u,v);
	SGT::add_ch(1,1,n,dfn[u],dfn[v],w);
	if(fa[u])SGT::add_fa(1,1,n,dfn[fa[u]],w);
	if(fa[fa[u]])SGT::add_fa(1,1,n,dfn[fa[fa[u]]],w);
	::w[u]+=w;if(fa[u])::w[fa[u]]+=w;
}

void Query(){
	int u=gi(),v=gi();ll ans=0;
	while(top[u]!=top[v]){
		if(d[top[u]]<d[top[v]])std::swap(u,v);
		ans+=SGT::query(1,1,n,dfn[top[u]],dfn[u]);u=fa[top[u]];
	}if(d[u]>d[v])std::swap(u,v);
	ans+=SGT::query(1,1,n,dfn[u],dfn[v]);
	if(fa[u])ans+=SGT::query_tg(1,1,n,dfn[fa[u]]);
	if(fa[fa[u]])ans+=SGT::query_tg(1,1,n,dfn[fa[fa[u]]]);
	ans+=w[u];if(fa[u])ans+=w[fa[u]];cout<<ans<<"\n";
}

void Main(){n=gi();
	for(int re i=1;i<n;++i)
		adde(gi(),gi());
	dfs1(1,0),dfs2(1,1);
	for(int re i=1;i<=n;++i)
		sm[fa[i]]+=(w[i]=gi());
	SGT::build(1,1,n);Q=gi();
	while(Q--)switch(gi()){
		case 1:Add();break;
		case 2:Query();break;
	}
}

inline void file(){
#ifdef zxyoi
	freopen("caterpillar.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("caterpillar.in","r",stdin);
	freopen("caterpillar.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}

T2:

n n n 个怪,你知道血量,但不知道血量对应的怪,你每次可以选择一个怪攻击,使其血量-1,血量为0的怪死亡,你知道每个怪之前被你打了几下以及哪些怪已经死亡。

请你求出在最优策略下,打死至少 m m m 只怪要用最少攻击次数。


读了一半还以为是交互题。

首先如果是交互题,写交互库的思路非常显然,按照被打的次数分集合,每次打的时候把集合中最大的一个拿出去放到下一个集合。

显然最优策略要面对就是这种情况。

排序,假设确定了我们要打的怪,怎么打?策略非常显然,按照血量排序,所有怪先打 a 1 a_1 a1 次可以把要打的最小怪打死,然后把没死的怪拿来打 a 2 − a 1 a_2-a_1 a2a1 次,依次类推。

我的 DP 和 std 有点不一样。先将 a i a_i ai 降序排序

设状态 f [ j ] [ i ] f[j][i] f[j][i] 表示考虑前 i i i 个怪,已经打死了 j j j 个怪需要的最少次数。

转移显然是 f [ j ] [ i ] = min ⁡ k < i { f [ j − 1 ] [ k ] + a [ i ] ∗ ( i − k ) } = a [ i ] ∗ i + min ⁡ k < i { f [ j − 1 ] [ k ] − a [ i ] ∗ k } f[j][i]=\min_{k<i}\{f[j-1][k]+a[i]*(i-k)\}=a[i]*i+\min_{k<i}\{f[j-1][k]-a[i]*k\} f[j][i]=mink<i{f[j1][k]+a[i](ik)}=a[i]i+mink<i{f[j1][k]a[i]k}

斜率优化即可,维护下凸壳。

代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=2e3+7;

int n,m;
int a[N]; 
ll f[N][N];

void Main(){
	scanf("%d%d",&n,&m);
	for(int re i=1;i<=n;++i)
		scanf("%d",a+i);
	std::sort(a+1,a+n+1);
	std::reverse(a+1,a+n+1);
	f[0][0]=0;
	std::fill(f[0]+1,f[0]+n+1,2e9);
	for(int re j=1;j<=m;++j){
		std::fill(f[j],f[j]+n+1,2e9);
		static ll x[N],y[N];int tp=1;
		x[tp]=0,y[tp]=f[j-1][0];
		for(int re i=1;i<=n;++i){
			auto calc=[i](int id){
				return y[id]-(ll)a[i]*x[id];
			};int l=1,r=tp;
			while(l<r){
				int mid=(l+r)>>1;
				if(calc(mid)<calc(mid+1))r=mid;
				else l=mid+1;
			}f[j][i]=(ll)a[i]*i+calc(l);
			auto crs=[&](int X,int Y){
				int x1=x[tp]-x[tp-1],y1=y[tp]-y[tp-1];
				int x2=X-x[tp],y2=Y-y[tp];
				return (ll)x1*y2-(ll)y1*x2;
			};
			while(tp>1&&crs(i,f[j-1][i])<=0)--tp;
			++tp;y[tp]=f[j-1][i],x[tp]=i;
		}
	}
	cout<<*std::max_element(f[m],f[m]+n+1,std::greater<ll>())<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("medium.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("medium.in","r",stdin);
	freopen("medium.out","w",stdout);
#endif
#endif
}signed main(){file();int T;scanf("%d",&T);while(T--)Main();return 0;}

T3

一个长为 n n n 的数列 a a a,删去 k k k 个数之后,向左补位得到长为 n − k n-k nk 的数列 b b b

请你求出 b i = i b_i=i bi=i 最多能出现多少次。


显然 最后可能满足条件的位置必然满足 a i ≤ n − k , a i ≥ i , i ≤ a i + k a_i\leq n-k,a_i\geq i,i\leq a_i+k aink,aii,iai+k,先把不合法的去掉。

容易发现我们求的是三维偏序的 LIS ,即 i < j , a i < a j , i − a i ≤ j − a j i<j,a_i<a_j,i-a_i\leq j-a_j i<j,ai<aj,iaijaj,容易发现满足后面两条的时候第一条自动满足,于是转化为二维偏序,一个log 解决。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
}using namespace IO;

using std::cerr;
using std::cout;
typedef std::pair<int,int> pii;
#define fi first
#define se second

cs int N=5e5+7;

int n,k;
int a[N],len;
std::vector<pii> vec;

void Main(){
	n=gi(),k=gi();
	for(int re i=1;i<=n;++i){
		int v=gi(),b=i-v;
		if(v>n-k||i<v||i>v+k)continue;
		vec.push_back(pii(b,v));
	}std::sort(vec.begin(),vec.end());a[0]=-1;
	for(size_t re i=0;i<vec.size();++i){
		int v=vec[i].se;
		if(v>a[len])a[++len]=v;
		else *std::lower_bound(a+1,a+len+1,v)=v;
	}cout<<len<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("maximize.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("maximize.in","r",stdin);
	freopen("maximize.out","w",stdout);
#endif
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值