【学习笔记】NOIP模拟赛25

9 篇文章 0 订阅
9 篇文章 0 订阅
本文探讨了动态规划和图论在解决信息技术问题中的关键作用,包括线性复杂度的最优化路径计算、仙人掌计数问题的高效算法以及二分图匹配在调度问题中的应用。通过实例解析了如何利用动态规划优化状态转移,以及如何运用图论解决复杂网络结构的问题,展示了这两种技术在信息技术领域的强大功能。
摘要由CSDN通过智能技术生成

据说是llsw出的题

我是没上200的丝薄

A. 遗忘十字路

f u , K f_{u,K} fu,K表示从 u u u出发,走 K K K次后价值和的最大值。

假设有 m m m个儿子,那么 f u , K = ∑ v f v , K / m + ∑ v ′ f v ′ , K / m + 1 f_{u,K}=\sum_v f_{v,K/m}+\sum_{v'}f_{v',K/m+1} fu,K=vfv,K/m+vfv,K/m+1

直接排序即可。应该可以感觉到状态数是线性的。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
int T,n;
ll a[500005],K;
vector<int>g[500005];
vector<ll>vec[500005];
vector<pair<int,ll>>id[500005];
inline int read() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar(); 
	}
	return x*f;
}
ll dfs(int u,int topf,ll K){
	int m=g[u].size()-(u!=1);if(m==0)return K*a[u];
	for(auto x:id[u])if(x.fi==K)return x.se;
	vec[u].clear();ll tot=0;
	for(auto v:g[u]){
		if(v!=topf){
			ll x=dfs(v,u,K/m),y=dfs(v,u,K/m+1);
			tot+=x,vec[u].pb(y-x);
		}
	}sort(vec[u].begin(),vec[u].end()),reverse(vec[u].begin(),vec[u].end());
	for(int j=0;j<K-K/m*m;j++)tot+=vec[u][j];
	id[u].pb(make_pair(K,tot+K*a[u]));
    return tot+K*a[u];
}
int main(){
	freopen("crossroad.in","r",stdin);
	freopen("crossroad.out","w",stdout);
	T=read();
	while(T--){
		n=read(),K=read();for(int i=1;i<=n;i++)a[i]=read(),g[i].clear(),id[i].clear();
		for(int i=1;i<n;i++){
			int u=read(),v=read();g[u].pb(v),g[v].pb(u);
		}printf("%lld\n",dfs(1,0,K));
	}
} 

B. 鹿角虫道

原题为「Gym102331C」Counting Cactus

姑且称之为仙人掌计数。

f ( u , S ) f(u,S) f(u,S)表示 u u u号节点为根,节点集合为 S S S的方案数。

g ( u , S ) g(u,S) g(u,S)表示 u u u号节点为根,且 u u u恰好只在一个环上,节点集合为 S S S的方案数。

d p ( u , v , S ) dp(u,v,S) dp(u,v,S)为钦定一个当前环的开头是 u u u,环尾扩展到了 v v v,节点集合为 S S S的方案数。

转移方程为:

1.1 1.1 1.1 v v v为编号最小的节点,并且 v ∈ T v\in T vT ( f v , T + g u , T + { u } ) × f u , S − T → f u , S (f_{v,T}+g_{u,T+\{u\}})\times f_{u,S-T}\to f_{u,S} (fv,T+gu,T+{u})×fu,STfu,S
1.2 1.2 1.2 u u u在环上,且环的终点为 v v v ( u , x ) ∈ E (u,x)\in E (u,x)E d p x , v , S − { u } → f u , S , g u , S dp_{x,v,S-\{u\}}\to f_{u,S},g_{u,S} dpx,v,S{u}fu,S,gu,S
1.3 1.3 1.3 对于 u → v u\to v uv的链,在 u u u上挂一个仙人掌,满足 ( u , x ) ∈ E (u,x)\in E (u,x)E u ∈ T u\in T uT x , v ∉ T x,v\notin T x,v/T d p x , v , S − T × f u , T → d p u , v , S dp_{x,v,S-T}\times f_{u,T}\to dp_{u,v,S} dpx,v,ST×fu,Tdpu,v,S

注意环有顺逆时针两种情况,所以要 / 2 /2 /2

复杂度 O ( 3 n n 3 ) O(3^nn^3) O(3nn3)。上界很松。通过固定转移系数 f u , T f_{u,T} fu,T可以优化至 O ( 3 n n 2 ) O(3^nn^2) O(3nn2)

有更好的方法,但是我不会。

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
const int mod=998244353;
int n,m,a[13][13];
ll inv2,f[13][1<<13],dp[13][13][1<<13],g[13][1<<13],res;
void add(ll &x,ll y){
	x=(x+y)%mod;
}
ll fpow(ll x,ll y){
	ll z(1);
	for(;y;y>>=1){
		if(y&1)z=z*x%mod;
		x=x*x%mod;
	}return z;
}
int main(){
	freopen("stag.in","r",stdin);
	freopen("stag.out","w",stdout);
	cin>>n>>m,inv2=fpow(2,mod-2);for(int i=1;i<=m;i++){
		int u,v;cin>>u>>v,u--,v--,a[u][v]=a[v][u]=1;
	}for(int i=0;i<n;i++)f[i][0]=1;
	for(int s=1;s<1<<n;s++){
		for(int i=0;i<n;i++){
			if(s>>i&1) {
				for(int j=0;j<n;j++){
					if(i==j||!(s>>j&1))continue;
					for(int k=0;k<n;k++){
						if(i==j||j==k||!(s>>k&1)||!a[i][k])continue;
						for(int s2=s-(1<<j)-(1<<k);s2;s2=(s2-1)&(s-(1<<j)-(1<<k))){
							if(s2>>i&1)add(dp[i][j][s],dp[k][j][s-s2]*f[i][s2-(1<<i)]);
						}
					}
					if(a[i][j]){
						for(int s2=s-(1<<j);s2;s2=(s2-1)&(s-(1<<j))){
							if(s2>>i&1)add(dp[i][j][s],f[i][s2-(1<<i)]*f[j][s-s2-(1<<j)]);
						}
					}
					
				}
			}
		}
		for(int i=0;i<n;i++){
			if(!(s>>i&1)){
				int j=0;while(!(s>>j&1))j++;
				for(int s2=s;s2;s2=(s2-1)&s){
					if(!(s2>>j&1))continue;
					add(f[i][s],g[i][s2]*f[i][s-s2]);
					for(int k=0;k<n;k++){
						if((s2>>k&1)&&a[i][k]){
							add(f[i][s],f[j][s2-(1<<j)]*f[i][s-s2]);
						}
					}
				}
				for(int j=0;j<n;j++){
					if(i==j||!(s>>j&1)||!a[i][j])continue;
					for(int k=0;k<n;k++){
						if(i==k||j==k||!(s>>k&1)||!a[i][k])continue;
						add(f[i][s],dp[k][j][s]*inv2);
						add(g[i][s],dp[k][j][s]*inv2);
					}
				}
			}
		}
	}
	cout<<f[0][(1<<n)-2];
}

C. 泪水之城

神秘多项式。。。

D. 生命血

原题为 「Gym102268D」Dates

应该可以看出来这是一个拟阵。

不妨将其转化为二分图匹配,使用 hall \text{hall} hall定理,只需关注右部点中的一段连续区间。问题转述为,对于任意 1 ≤ i ≤ j ≤ m 1\le i\le j\le m 1ijm s r j − s l i − 1 ≥ j − i + 1 s_{r_j}-s_{l_i-1}\ge j-i+1 srjsli1ji+1,其中 s i s_i si表示 a i a_i ai的前缀和。

不妨将话说明白些。分参易得 s r j − j ≥ s l i − 1 − i + 1 s_{r_j}-j\ge s_{l_i-1}-i+1 srjjsli1i+1

我们将其视作位置 i i i的排名 f i f_i fi, g i g_i gi。那么对于任意 j ≥ i j\ge i ji f j ≥ g i f_j\ge g_i fjgi。每次插入一个操作只会影响 i i i, j j j(相对位置),因此前面的排名不变,设插入位置为 k k k,只需判断 max ⁡ i = 1 k g i \max_{i=1}^k g_i maxi=1kgi min ⁡ i = k m f i \min_{i=k}^mf_i mini=kmfi的大小关系,因此用两颗线段树维护。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define int ll
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,m,T,L[300005],R[300005],a[300005],s[300005],bit[300005];
struct node{
	int dat,max,min;
}t[300005*4];
struct node2{
	int v,l,r,id;
	bool operator <(const node2 &a)const{
		return v>a.v;
	}
}q[300005];
vector<int>vec;
void add(int x,int k){
	for(;x<=m;x+=x&-x)bit[x]+=k;
}
int ask(int x){
	int tot(0);for(;x;x-=x&-x)tot+=bit[x];return tot;
}
void pushup(int p){
	t[p].max=max(t[p<<1].max,t[p<<1|1].max);
	t[p].min=min(t[p<<1].min,t[p<<1|1].min);
}
void dat(int p,int x){
	t[p].max+=x,t[p].min+=x,t[p].dat+=x;
}
void pushdown(int p){
	if(t[p].dat)dat(p<<1,t[p].dat),dat(p<<1|1,t[p].dat),t[p].dat=0;
}
void build(int p,int l,int r){
	t[p].dat=0;t[p].min=inf,t[p].max=-inf;
	if(l==r)return;
	int mid=l+r>>1;build(p<<1,l,mid),build(p<<1|1,mid+1,r);
}
void mdy(int p,int l,int r,int x,int y,int z){
	if(l==r){t[p].min=y,t[p].max=z;return;}
	pushdown(p);int mid=l+r>>1;
	x<=mid?mdy(p<<1,l,mid,x,y,z):mdy(p<<1|1,mid+1,r,x,y,z);
	pushup(p);
}
void upd(int p,int l,int r,int ql,int qr,int x){
	if(ql>qr)return;
	if(ql<=l&&r<=qr){dat(p,x);return;}
	pushdown(p);int mid=l+r>>1;
	if(ql<=mid)upd(p<<1,l,mid,ql,qr,x);if(mid<qr)upd(p<<1|1,mid+1,r,ql,qr,x);
	pushup(p); 
}
int qry(int p,int l,int r,int ql,int qr,int op){
	if(ql>qr)return op?-inf:inf;
	if(ql<=l&&r<=qr)return op?t[p].max:t[p].min;
	pushdown(p);int mid=l+r>>1;
	if(qr<=mid)return qry(p<<1,l,mid,ql,qr,op);
	if(mid<ql)return qry(p<<1|1,mid+1,r,ql,qr,op);
	if(op==0)return min(qry(p<<1,l,mid,ql,qr,op),qry(p<<1|1,mid+1,r,ql,qr,op));
	return max(qry(p<<1,l,mid,ql,qr,op),qry(p<<1|1,mid+1,r,ql,qr,op));
}
signed main(){
	freopen("lifeblood.in","r",stdin);
	freopen("lifeblood.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i],s[i]=s[i-1]+a[i];
		for(int i=1;i<=m;i++){
			int l,r,v;cin>>l>>r>>v;
			q[i]={v,l,r,i},L[i]=l,R[i]=r;
		}sort(q+1,q+1+m);
		vec.clear();ll res=0;build(1,1,m);for(int i=1;i<=m;i++)bit[i]=0;
		for(int i=1;i<=m;i++){
			int l=q[i].l,r=q[i].r,k=q[i].id;
			mdy(1,1,m,k,s[r]-ask(k),s[l-1]-ask(k)+1);
			upd(1,1,m,k+1,m,-1);
			if(qry(1,1,m,1,k,1)>qry(1,1,m,k,m,0)){
				mdy(1,1,m,k,inf,-inf),upd(1,1,m,k+1,m,1);
			}
			else {
				vec.pb(k),res+=q[i].v,add(k,1);
			}
		}cout<<res<<"\n"<<vec.size()<<"\n";
		if(vec.size()){
			for(int i=0;i<vec.size()-1;i++){
				cout<<vec[i]<<' ';
			}cout<<vec.back();
			cout<<"\n";
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值