2019.07.10【NOIP提高组】模拟 A 组

博客围绕三道OJ题目展开,包括BZOJ 3257、JZOJ 3347的树的难题,采用树形dp求解;BZOJ 3258、JZOJ 3348的秘密任务,是求最小割问题,用tarjan解决;JZOJ 3379查询为多组询问,是主席树裸题,还给出了各题代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BZOJ 3257 JZOJ 3347 树的难题

题目


分析

树形dp,设 d p [ x ] [ i ] [ j ] dp[x][i][j] dp[x][i][j]表示第 x x x个点,有无黑点,白点个数为 j j j,若 j ≥ 2 j\geq 2 j2视作2的最小代价
那么 d p [ x ] [ i ] [ j ] = ∑ s o n ∈ i min ⁡ { d p [ s o n ] [ i ′ ] [ j ′ ] } + w dp[x][i][j]=\sum_{son\in i}\min\{dp[son][i'][j']\}+w dp[x][i][j]=sonimin{dp[son][i][j]}+w


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
typedef long long ll;
const ll inf=1e14,N=3e5+1;
struct node{int y,w,next;}e[N<<1|1];
int k,ls[N],a[N],n,head,tail; ll dp[N][2][3];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void add(int x,int y,int w){
	e[++k]=(node){y,w,ls[x]}; ls[x]=k;
	e[++k]=(node){x,w,ls[y]}; ls[y]=k;	
}
inline ll min(ll a,ll b){return a<b?a:b;}
signed main(){
	for (rr int t=iut();t;--t){
		k=head=tail=1; memset(ls,0,sizeof(ls));
		n=iut(); rr int stx[n+1],sfa[n+1];
		for (rr int i=1;i<=n;++i) a[i]=iut();
		for (rr int i=1,u,v;i<n;++i)
		    u=iut(),v=iut(),add(u,v,iut());
		stx[1]=sfa[1]=1;
		while (head<=tail){
			rr int x=stx[head],fa=sfa[head++];
			for (rr int i=ls[x];i;i=e[i].next)
			    if (e[i].y!=fa) stx[++tail]=e[i].y,sfa[tail]=x;
		}
		for (rr int h=tail;h;--h){
			rr int x=stx[h],fa=sfa[h];
	        for (rr int p1=0;p1<2;++p1)
	        for (rr int p2=0;p2<3;++p2) dp[x][p1][p2]=inf;
	        dp[x][a[x]==0][a[x]==1]=0;
	        for (rr int i=ls[x];i;i=e[i].next)
	        if (e[i].y!=fa){
		        rr ll f[2][3];
		        for (rr int p1=0;p1<2;++p1)
		        for (rr int p2=0;p2<3;++p2) f[p1][p2]=inf;
				for (rr int p1=0;p1<2;++p1)
				for (rr int p2=0;p2<3;++p2) if (dp[x][p1][p2]!=inf)
				for (rr int p3=0;p3<2;++p3)
				for (rr int j=0;j<3;++j) if (dp[e[i].y][p3][j]!=inf){
			        rr int z1=p1+p3>=1,z2=min(p2+j,2);
					f[z1][z2]=min(f[z1][z2],dp[x][p1][p2]+dp[e[i].y][p3][j]);
			        if (!p3||j<2) f[p1][p2]=min(f[p1][p2],dp[x][p1][p2]+dp[e[i].y][p3][j]+e[i].w);
		        }
				memcpy(dp[x],f,sizeof(f));
		    }
		}
		printf("%lld\n",min(min(min(dp[1][0][0],dp[1][0][1]),min(dp[1][0][2],dp[1][1][0])),dp[1][1][1]));
    }
	return 0;
}

BZOJ 3258 JZOJ 3348 秘密任务

题目


分析

这是一道求最小割的题目,对于第二问,最小割是否唯一,当且仅当割边的两头属于起点或终点,用tarjan解决


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
#define cle(TEMP,_T) memset(TEMP,_T,sizeof(TEMP))
#define min(a,b) ((a)<(b)?(a):(b))
#define rr register
using namespace std;
typedef unsigned uit;
const uit inf=4e9+101;
struct node{uit y,w,next;}e[8011],E[8011]; bool v[401]; long long dis[401];
uit k1,k2,ls[401],hs[401],dfn[401],low[401],stack[401],a[401],dcc[401],s,t,n,m,top,DCC,ans,flag; 
inline signed iut(){
	rr uit ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void Add(uit x,uit y,uit w){
	E[++k2]=(node){y,w,hs[x]},hs[x]=k2;
	E[++k2]=(node){x,w,hs[y]},hs[y]=k2;
}
inline void add(uit x,uit y,uit w){
	e[++k1]=(node){y,w,ls[x]},ls[x]=k1;
	e[++k1]=(node){x,0,ls[y]},ls[y]=k1;
}
inline void spfa(){
	for (rr uit i=1;i<=n;++i) dis[i]=1e14,v[i]=0;
	dis[1]=0,v[1]=1; rr deque<uit>q; q.push_front(1);
	while (q.size()){
		rr uit x=q.front(); q.pop_front();
		for (rr uit i=hs[x];i;i=E[i].next)
		if (dis[E[i].y]>dis[x]+E[i].w){
			dis[E[i].y]=dis[x]+E[i].w;
			if (!v[E[i].y]){
				v[E[i].y]=1;
				if (q.size()&&dis[E[i].y]<dis[q.front()]) q.push_front(E[i].y);
				    else q.push_back(E[i].y);
			}
		}
		v[x]=0;
	}
}
inline void Dfs(uit x){
	if (v[x]) return; v[x]=1;
	for (rr uit i=hs[x];i;i=E[i].next)
	if (dis[x]==dis[E[i].y]+E[i].w)
	    add(E[i].y,x,min(a[x],a[E[i].y])),Dfs(E[i].y);
}
inline signed bfs(uit s){
    for (rr uit i=1;i<=t;++i) dis[i]=0;
    queue<uit>q; q.push(s); dis[s]=1;
    while (q.size()){
        rr uit x=q.front(); q.pop();
        for (rr uit i=ls[x];i;i=e[i].next)
        if (e[i].w>0&&!dis[e[i].y]){
            dis[e[i].y]=dis[x]+1;
            if (e[i].y==t) return 1;
            q.push(e[i].y);
        }
    }
    return 0;
}
inline signed dfs(uit x,uit now){
    if (x==t||!now) return now;
    rr uit rest=0,f;
    for (rr uit i=ls[x];i;i=e[i].next)
    if (e[i].w>0&&dis[e[i].y]==dis[x]+1){
        rest+=(f=dfs(e[i].y,min(now-rest,e[i].w)));
        e[i].w-=f; e[i^1].w+=f;
        if (now==rest) return rest;
    }
    if (!rest) dis[x]=0;
    return rest;
}
inline signed dinic(){
	rr uit ans=0;
	while (bfs(s)) ans+=dfs(s,inf);
	return ans;
}
inline void tarjan(uit x){
	dfn[x]=low[x]=++dfn[0],stack[++top]=x;
	for (rr uit i=ls[x];i;i=e[i].next)
	if (e[i].w){
		if (!dfn[e[i].y]) tarjan(e[i].y),low[x]=min(low[x],low[e[i].y]);
		    else if (!dcc[e[i].y]) low[x]=min(low[x],dfn[e[i].y]);
	}
	if (dfn[x]==low[x]){
	    ++DCC;
		for (uit y;y!=x;--top) dcc[y=stack[top]]=DCC;
	}	
}
signed main(){
	for (rr uit T=iut();T;--T){
		cle(hs,0),cle(ls,0),cle(dfn,0),cle(low,0),cle(dcc,0),
		n=iut(),m=iut(),k1=k2=1,flag=DCC=top=0;
		for (rr uit i=1;i<n;++i) a[i]=iut(); a[n]=inf;
		for (rr uit i=1,x,y;i<=m;++i) x=iut(),y=iut(),Add(x,y,iut());
		spfa(),s=1,t=n,Dfs(n),ans=dinic();
		for (rr uit i=1;i<=n;++i) if (!dfn[i]) tarjan(i);
		for (rr uit i=2;i<=k1;i+=2) if (!e[i].w){
			if (dcc[e[i].y]!=dcc[e[i^1].y]&&!(dcc[e[i^1].y]==dcc[1]&&dcc[e[i].y]==dcc[n])) flag=1;
			if (dcc[e[i^1].y]==dcc[1]&&dcc[e[i].y]==dcc[n]&&a[e[i].y]==a[e[i^1].y]) flag=1;
		}
		!flag?printf("Yes "):printf("No "); printf("%u\n",ans); 
	}
	return 0;
}

JZOJ 3379 查询

题目

多组询问,询问把最多5个区间的数扔入multiset,问第 k k k


分析

主席树裸题,不多解释


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=200101;
int a[N],b[N],rt[N],L[5],R[5],tot,n,q,m;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
struct Chair{
    int w[N*20],ls[N*20],rs[N*20],cnt;
    inline void build(int &rt,int l,int r){
        w[rt=++cnt]=0; rr int mid=(l+r)>>1;
        if (l<r) build(ls[rt],l,mid),build(rs[rt],mid+1,r); 
    }
    inline void update(int &rt,int l,int r,int k){
        rr int trt=++cnt,mid=(l+r)>>1;
        ls[trt]=ls[rt],rs[trt]=rs[rt],w[trt]=w[rt]+1,rt=trt;
        if (l==r) return;
        k<=mid?update(ls[trt],l,mid,k):update(rs[trt],mid+1,r,k);
    }
    inline signed query(int l,int r,int k){
    	if (l==r) return l;
    	rr int x=0,mid=(l+r)>>1;
    	for (rr int i=0;i<tot;++i) x+=w[ls[R[i]]]-w[ls[L[i]]];
    	for (rr int i=0;i<tot;++i)
    	    x>=k?(L[i]=ls[L[i]],R[i]=ls[R[i]]):(L[i]=rs[L[i]],R[i]=rs[R[i]]);
    	return x>=k?query(l,mid,k):query(mid+1,r,k-x);
    } 
}Tre;
signed main(){
	n=iut(); q=iut();
    for (rr int i=1;i<=n;++i) b[i]=a[i]=iut();
    sort(b+1,b+1+n); m=unique(b+1,b+1+n)-b-1;
    Tre.cnt=0; Tre.build(rt[0],1,m);
    for (rr int i=1;i<=n;++i)
        Tre.update(rt[i]=rt[i-1],1,m,lower_bound(b+1,b+1+m,a[i])-b);
    while (q--){
    	tot=iut(); rr int k=iut();
    	for (rr int i=0;i<tot;++i) L[i]=rt[iut()-1],R[i]=rt[iut()];
    	print(b[Tre.query(1,m,k)]),putchar(10);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值