230927校内赛

T1 集合

题解

很明显的一道树形 dp

我也没看出来

dalao们说有一道 ddp 的题转移方式一模一样

于是都切了,就我是sb

首先有一个非常明显的性质在于所有的被选的点一定是可以构成一颗连通子树的

最终选取的 k 个点一定是从这颗子树中最远点对的一个点沿着这颗子树直径同时遍历接在直径上的每一棵子树来到另一个点毛估估一下

于是可以用这条直径的端点情况与遍历的点的个数来设计状态

Cx,m 表示的是起点和终点皆在 x 子树内且不为 x,在子树内遍历了 m 个点的最小代价

Fx,m 表示的是起点和终点有一个点在 x 子树内且另一点为 x,在子树内遍历了 m 个点的最小代价

Bx,m 表示的是起点和终点皆为 x,在子树内遍历了 m 个点的最小代价

状态转移方程式见代码,不难理解的

直接 DFS 时从大到小枚举一个点的子树大小之和,并且从大到小枚举当前子树大小,以此更新当前节点答案

这样枚举一个点对时会在他们的 lca 处进行更新

时间复杂度为 O(n2)

#include<bits/stdc++.h>
#define N 3010
using namespace std;
struct edge{
	int v,ne,w;
}e[N<<1];
int n,k,cnt,ans,h[N],f[N][N],b[N][N],c[N][N],sz[N];
void add(int u,int v,int w){
	e[++cnt].v = v;
	e[cnt].w = w;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
void dp(int x,int fa){
	sz[x] = 1;
	f[x][1] = c[x][1] = b[x][1] = 0;
	for(int i = h[x];i;i = e[i].ne){
		int v = e[i].v;
		if(v==fa) continue;
		dp(v,x);
		for(int y = sz[x];y>0;y--){
			for(int j = sz[v];j>0;j--){
				b[x][j+y] = min(b[x][j+y],b[v][j]+b[x][y]+e[i].w*2);
				f[x][j+y] = min({f[x][j+y],b[v][j]+f[x][y]+e[i].w*2,
					f[v][j]+b[x][y]+e[i].w});
				c[x][j+y] = min({c[x][j+y],c[x][y]+b[v][j]+e[i].w*2,
					c[v][j]+b[x][y]+e[i].w*2,f[x][y]+f[v][j]+e[i].w});
			}
		}
		sz[x]+=sz[v];
	}
	ans = min({ans,c[x][k],b[x][k],f[x][k]});
}
signed main(){
	freopen("set.in","r",stdin);
	freopen("set.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	ans = 0x3f3f3f3f;
	memset(f,0x3f,sizeof(f));
	memset(c,0x3f,sizeof(c));
	memset(b,0x3f,sizeof(b));
	cin>>n>>k;
	for(int i = 1;i<n;i++){
		int x,y,w;
		cin>>x>>y>>w;
		add(x,y,w);add(y,x,w);
	}
	dp(1,0);
	cout<<ans;
	return 0;
}

日常讨厌 dp

易水决

名字不错

pPbkn9f.jpg

题解

一道挺明显的贪心

有sb考试时侯想第一题去了放走了 100 分

首先思考单独对一个操作进行贪心

我们可以先给所有的 a 机器假设为给一个原材料并放入堆中

再将堆顶取出,将其再加上自身的时间后再放回去

这样进行 l 次操作即可

正确性?因为所有的都是放在堆里的,所以只要将最小的增加直到其不为最小一定是最优

对于 b 也是一样的

那么两个操作合并起来如何最大时间最小

将两个操作一个正序一个逆序拼接起来

不懂的可以看代码

正确性?对于两对操作来说,如果你要更换两个a,b 的对应,那么一定会有一对用时比原来这两对中最大的更大

时间复杂度因为有堆所以是 O(nlogn)

#include<bits/stdc++.h>
#define N 100010
#define int long long
using namespace std;
struct node{
	int id,t;
	bool operator <(node b)const{
		return t>b.t;
	}
};
int l,n,m,a[N],b[N],t[N*10];
signed main(){
	freopen("farewell.in","r",stdin);
	freopen("farewell.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>l>>n>>m;
	for(int i = 1;i<=n;i++)
		cin>>a[i];
	for(int i = 1;i<=m;i++)
		cin>>b[i];
	priority_queue<node>p,q;
	for(int i = 1;i<=n;i++)
		q.push((node){i,a[i]});
	for(int i = 1;i<=m;i++) 
		p.push((node){i,b[i]});
	for(int i = 1;i<=l;i++){
		int id = q.top().id,x = q.top().t;q.pop();
		t[i] = x;
		q.push((node){id,x+a[id]});
	}
	int ans = 0;
	for(int i = l;i>0;i--){
		int id = p.top().id,x = p.top().t;p.pop();
		p.push((node){id,x+b[id]});
		ans = max(ans,t[i]+x);
	}
	cout<<ans;
	return 0;
}

T3 pockets

pPbA0Rf.jpg

题解

对于两个区间的快速比较操作,通常会考虑到哈希操作,修改后就重新哈希一次

显然这么做的复杂度只适合于修改少询问多时

那么自然考虑到用线段树维护哈希

如果没有取模操作,那么数值只会变大

对于区间加操作,显然有区间哈希值增加 i=lrbi

对于变 0 操作就暴力维护

显然还是过不去的

需要优化的仍然是区间加操作

区间加有一个很简单的优化:差分!

比较时也可以用差分数组来比较

那么就只用单点修改并维护了

询问时不要忘了除了相同还要判断i=l+1rBi%k==0

区间哈希和这个新增加的条件都可以线段树维护

时间复杂度 O(nlogn)

#include<bits/stdc++.h>
#define N 500010
#define int unsigned long long
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
int n,q,mod,a[N],b[N];
pii pw[N],bas = {1331,233};
pii operator *(pii a,pii b){
	return {1ll*a.fi*b.fi,1ll*a.se*b.se};
}
pii operator +(pii a,pii b){
	return {a.fi+b.fi,a.se+b.se};
}
bool operator ==(pii a,pii b){
	return a.fi==b.fi&&a.se==b.se;
}
struct node{
	pii h;
	int len;
	node operator +(node b){
		return (node){h*pw[b.len]+b.h,len+b.len};
	}
	bool operator ==(node b){
		return h==b.h&&len==b.len;
	}
};
struct seg{
	#define lc (p<<1)
	#define rc ((p<<1)|1)
	#define mid ((l+r)>>1)
	node t[N<<2];
	int vl[N<<2];
	void build(int p,int l,int r){
		if(l==r){
			vl[p] = b[l];
			t[p] = (node){{b[l],b[l]},1};
			return ;
		}
		build(lc,l,mid);
		build(rc,mid+1,r);
		t[p] = t[lc]+t[rc];
		vl[p] = (vl[lc]+vl[rc])%mod;
	}
	node query(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return t[p];
		if(qr<=mid) return query(lc,l,mid,ql,qr);
		if(ql>mid) return query(rc,mid+1,r,ql,qr);
		return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
	}
	int querysm(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return vl[p];
		int res = 0;
		if(ql<=mid) res = (res+querysm(lc,l,mid,ql,qr))%mod;
		if(qr>mid) res = (res+querysm(rc,mid+1,r,ql,qr))%mod;
		return res;
	}
	void update(int p,int l,int r,int v){
		if(l==r){
			vl[p] = b[l];
			t[p] = (node){{b[l],b[l]},1};
			return ;
		}
		if(v<=mid) update(lc,l,mid,v);
		else update(rc,mid+1,r,v);
		t[p] = t[lc]+t[rc];
		vl[p] = (vl[lc]+vl[rc])%mod;
	}
}T;
signed main(){
	freopen("pockets.in","r",stdin);
	freopen("pockets.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	pw[0] = {1,1};
	for(int i = 1;i<N;i++) pw[i] = pw[i-1]*bas;
	cin>>n>>q>>mod;
	for(int i = 1;i<=n;i++){
		cin>>a[i];
		b[i] = (a[i]-a[i-1]+mod)%mod;
	}
	T.build(1,1,n);
	while(q--){
		int op,x,y,k;
		cin>>op>>x>>y>>k;
		if(op==1){
			b[x] = (b[x]+k)%mod;
			if(y<n) b[y+1] = (b[y+1]+mod-k)%mod;
			T.update(1,1,n,x);
			if(y<n) T.update(1,1,n,y+1);
		}else{
			int ok = 1;
			if(T.querysm(1,1,n,x+1,y)!=0) ok = 0;
			if(k>1)
				if(!(T.query(1,1,n,x+1,x+k-1)==T.query(1,1,n,y+1,y+k-1)))
					ok = 0;
			if(ok) cout<<"ye5\n";
			else cout<<"n0\n";
		}
	}
	return 0;
}

T4 复制粘贴 3

洛谷传送门

loj传送门

我觉得洛谷的第三篇题解(写的是 dp 的那个)讲的很清楚了,在此不作赘述

#include<bits/stdc++.h>
#define int unsigned long long
#define inf 1e17
#define N 2510
using namespace std;
int n,a,b,c,tot,tk,pre[N][N],f[N][N],h[N],pw[N],p = 131;
string s;
unordered_map<int,int>mp;
void prework(){
	pw[0] = 1;
	for(int i = 1;i<=N-4;i++)
		pw[i] = pw[i-1]*p;
	for(int i = 1;i<=n;i++)
		h[i] = (h[i-1]*p+(s[i-1]-'a'+1));
}
int gethash(int l,int r){
	return (h[r]-h[l-1]*pw[r-l+1]);
}
signed main(){
	freopen("copy.in","r",stdin);
	freopen("copy.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>s>>a>>b>>c;
	for(int i = 0;i<=n;i++)
		for(int j = 0;j<=n;j++)
			f[i][j] = inf;
	prework();
	for(int len = 1;len<=n;len++){
		mp.clear();
		for(int r = len;r<=n;r++){
			int l = r-len+1;
			if(l-len>=1) mp[gethash(l-len,l-1)] = l-len;
			pre[l][r] = mp[gethash(l,r)];
		}
	}
	for(int i = 1;i<=n;i++) f[i][i] = a;
	for(int len = 1;len<n;len++){
		for(int r = len;r<=n;r++){
			int l = r-len+1;
			f[l-1][r] = min(f[l-1][r],f[l][r]+a);
			f[l][r+1] = min(f[l][r+1],f[l][r]+a);
			int cc = 1,t = pre[l][r];
			while(t){
				cc++;
				f[t][r] = min(f[t][r],f[l][r]+b+cc*c+(r-t+1-cc*len)*a);
				t = pre[t][t+len-1];
			}
		}
	}
	cout<<f[1][n];
	return 0;
}
posted @   cztq  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示