191003NOI模拟题解

T1:
题目大意:给你一张图,每条边有边权,要求保留边权和尽量大的边使得每个点至多有一条出边和至多一条入边,点数200,边数5000
解法:显然的二分图最小费用流,可行流即可不需要最大流

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e6+5,INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f;
int vis[N<<1],head[N],nxt[N<<1],c[N<<1],e[N<<1],tot=1;
inline void add(int x,int y,int z,int w){
	vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;e[tot]=w;
	vis[++tot]=x;nxt[tot]=head[y];head[y]=tot;c[tot]=0;e[tot]=-w;
}
ll d[N];
int pt[N];
int s,t;
inline bool spfa(){
	queue<int>q;q.push(s);
	for(int i=1;i<=t;i++) d[i]=inf;
	d[s]=0;
	while(!q.empty()){
		int x=q.front();q.pop();pt[x]=0;
		for(int i=head[x];i;i=nxt[i]){
			int y=vis[i];
			if(c[i]>0 && d[y]>d[x]+e[i]){
				d[y]=d[x]+e[i];
				if(!pt[y]){q.push(y);pt[y]=1;}
			}
		}
	}
	return d[t]!=inf;
}
int mxflow=0;ll mncost=0;
int cur[N];
ll ans=INF;
inline int dfs(int v,int flow){
	if(v==t){mxflow+=flow;return flow;}
	int res=0;pt[v]=1;
	for(int i=cur[v];i;i=nxt[i]){
		int y=vis[i];
		if(!pt[y] && d[y]==d[v]+e[i] && c[i]){
			cur[v]=i;
			int k=dfs(y,min(flow-res,c[i]));
			c[i]-=k;c[i^1]+=k;res+=k;mncost+=1ll*k*e[i];
			if(res==flow) break;
		}
	}
	pt[v]=0;
	return res;
}
inline void mcmf(){while(spfa()) memcpy(cur,head,sizeof(head)),dfs(s,INF),ans=min(ans,mncost);}
ll sum=0;
signed main(){
	int n=read(),m=read();
	for(int x,y,i=1;i<=m;i++){
		x=read(),y=read();ll z=read();
		sum+=z;if(z<=0) continue;
		add(x,y+n,1,-z);
	}
	s=0,t=n+n+1;
	for(int i=1;i<=n;i++) add(s,i,1,0);
	for(int i=1;i<=n;i++) add(i+n,t,1,0);
	mcmf();cout<<sum+ans;
	return 0;
}

T2:给你一个最大50x50的矩阵,每个点有一个要求的颜色,或者这个点禁止粉刷,现在可以用任意颜色粉刷任意一行或列,求每个点粉刷成要求颜色的最少次数并输出方案
解法:显然最多粉刷n+m-1次,那就是有一行没有被刷到(或者列,可以通过旋转矩阵再做一遍处理),枚举哪一行没被刷到,然后可以通过这一行的格子要求的颜色分析出某些列刷成了什么颜色,然后这些列上与这个格子的颜色不同的就是后来又被刷了一次,这样可以建出数个限制,如果有环显然无解,所以拓扑排序即可

Code:

#include<bits/stdc++.h>
#define pb push_back 
#define db double
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
namespace topsort{
	const int N=205,M=40005;
	int vis[M<<1],head[N],nxt[M<<1],tot=0;
	int in[N],pt[N];
	vector<int>rk;
	inline void clear(){memset(head,0,sizeof(head));tot=0;memset(in,0,sizeof(in));}
	inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;++in[y];}
	inline bool topsort(int n){
		queue<int>q;rk.clear();
		for(int i=1;i<=n;i++) if(pt[i] && !in[i]) q.push(i);
		while(!q.empty()){
			int x=q.front();q.pop();rk.pb(x);
			for(int i=head[x];i;i=nxt[i]){
				int y=vis[i];--in[y];
				if(!in[y]) q.push(y);
			}
		}
		for(int i=1;i<=n;i++) if(in[i]) return 0;
		return 1;
	}
}
using namespace topsort;
const int NN=55;
int col[NN][NN],tmp[NN][NN];
int n,m,c;
struct info{
	int op,num,col;
	info(){}
	info(int _op,int _num,int _col):op(_op),num(_num),col(_col){}
};
vector<info>ans,now;
int R[NN],C[NN],flag=0;
inline bool check(int x){
	for(int i=1;i<=m;i++) C[i]=col[x][i];
	for(int i=1;i<=n;i++) R[i]=-1;
	R[x]=0;clear();
	for(int i=1;i<=n;i++){
		if(x==i) continue;
		for(int j=1;j<=m;j++) if(C[j]!=col[i][j]){
			if(C[j]!=0 && col[i][j]==0) return false;
			if(R[i]==-1) R[i]=col[i][j];
			else if(R[i]!=col[i][j]) return false;
		}
		if(R[i]==-1) R[i]=0;
		if(R[i]==0) continue;
		for(int j=1;j<=m;j++){
			if(C[j]==0) continue;
			if(C[j]!=col[i][j]) add(j+n,i);
			else if(C[j]==col[i][j] && C[j]!=R[i]) add(i,j+n);
		}
	}
	for(int i=1;i<=n;i++) pt[i]=R[i]!=0;
	for(int i=1;i<=m;i++) pt[n+i]=C[i]!=0;
	if(!topsort::topsort(n+m)) return false;
	now.clear();
	for(int i=0;i<rk.size();i++){
		if(rk[i]<=n) now.pb(info(0,rk[i],R[rk[i]]));
		else now.pb(info(1,rk[i]-n,C[rk[i]-n]));
	}
	return true;
}
inline void file(){freopen("airline.in","r",stdin);freopen("airline.out","w",stdout);}
int main(){
	n=read();m=read();c=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) col[i][j]=read();
	for(int i=1;i<=n;i++) if(check(i) && (!flag || ans.size()>now.size())) ans=now,flag=1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) tmp[j][i]=col[i][j]; 
	for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) col[i][j]=tmp[i][j]; 
	swap(n,m);
	for(int i=1;i<=n;i++) if(check(i) && (!flag || ans.size()>now.size())) ans=now,flag=2;
	if(flag){
		if(flag==2) for(int i=0;i<ans.size();i++) ans[i].op^=1;
		cout<<ans.size()<<"\n";
		for(int i=0;i<ans.size();i++) cout<<(ans[i].op?"C":"R")<<" "<<ans[i].num<<" "<<ans[i].col<<"\n"; 
	}
	else puts("-1");
	return 0;
}

T3:有一棵树,1为根,初始每个叶子结点被占领,每个被占领的点每秒会产出一个士兵,士兵会一直往根节点走,走到根节点后自动消失,走到一个没有被占领的节点时会对其造成1点损伤,当一个没被占领的节点受到等同于其防御值的伤害后会被占领,求所有节点最晚多久被全部占领,规模100000
解法:考虑dpdpdp[i]dp[i]表示ii点被占领的时间,则ii点的值由其子树中所有点转移而来,具体地,把子树所有点的dp值排序,则排序后两个相邻时刻之差乘上对应的时刻数就是这一段时间造成的伤害值,那么求个前缀和就可以转移
则我们需要一个数据结构支持:
1.排序
2.二分查询前缀和
3.合并
显然是平衡树,splay启发式合并即可,也可以用dsu on tree

Code:

#include<bits/stdc++.h>
#define db double
#define ll long long
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=5e5+5;
int n,a[N];
int vis[N<<1],head[N<<1],nxt[N<<1],c[N<<1],tot=0;
inline void add(int x,int y,int z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
const int rt=1;
int root[N],que[N];
struct Splay{
	int fa[N],c[N][2];
	ll key[N],sum[N];
	int size[N],sz;
	inline void pushup(int d){
		sum[d]=sum[c[d][0]]+sum[c[d][1]]+key[d];
		size[d]=size[c[d][0]]+size[c[d][1]]+1;
	}
	inline void rotate(int p,int x){
		int mark=p==c[x][1],y=c[p][mark^1];
		int z=fa[x];
		if(x==c[z][0])c[z][0]=p;
		if(x==c[z][1])c[z][1]=p;
		if(y!=0)fa[y]=x;
		fa[p]=z;c[p][mark^1]=x;
		fa[x]=p;c[x][mark]=y;
		pushup(x);
	}
	inline void splay(int p,int &rt){
		while(fa[p]){
			int x=fa[p],y=fa[x];
			if(y==0)rotate(p,x);
			else if(x==c[y][0]^p==c[x][0]) rotate(p,x),rotate(p,y);
			else rotate(x,y),rotate(p,x);
		}
		rt=p;
		pushup(p);
	}
	int newnode(){++sz;c[sz][0]=c[sz][1]=0;fa[sz]=0;return sz;}
	inline void insert(ll k,int y,int pos){
		int now=root[y],f=now,mark=0;
		while(now){
			f=now;
			if(key[now]<=k)now=c[now][mark=1];
			else now=c[now][mark=0];
		}
		now=pos;
		fa[now]=f;if(f!=0)c[f][mark]=now;
		c[now][0]=c[now][1]=0;key[now]=k;
		splay(now,root[y]);
	}
	inline ll upperbound(ll k,int y){
		int now=root[y],f=now;
		while(now){
			if(key[now]<=k){
				if(f==root[y] && key[f]<=key[now]) f=now;
				now=c[now][1];
			}
			else f=now,now=c[now][0];
		}
		splay(f,root[y]);
		return k*size[c[f][0]]-sum[c[f][0]];
	}
	inline void merge(int x,int y){
		if(size[root[x]]>size[root[y]]) swap(root[x],root[y]);
		int head=0,tail=1;
		que[head]=root[x];
		while(head!=tail){
			int x=que[head++];
			if(c[x][0])que[tail++]=c[x][0];
			if(c[x][1])que[tail++]=c[x][1];
			insert(key[x],y,x);
		}
	}
}tt;
int fa[N];
ll dis[N],mx[N],t[N];
void dfs(int u){
	for(int i=head[u];i;i=nxt[i]){
		int v=vis[i];
		if(v==fa[u]) continue;
		fa[v]=u;dis[v]=dis[u]+c[i];
		dfs(v);
		mx[u]=max(mx[v]+c[i],mx[u]);
	}
}
void dfs2(int u){
	tt.insert(1ll<<60,u,tt.newnode());
	for(int i=head[u];i;i=nxt[i]){
		int v=vis[i];
		if(v==fa[u])continue;
		dfs2(v);tt.merge(v,u);
	}
	ll l=0,r=mx[u]+a[u];
	while(l<r){
		ll mid=l+r>>1;
		if(tt.upperbound(mid+dis[u],u)>=a[u]) r=mid;
		else l=mid+1;
	}
	t[u]=r;
	tt.insert(r+dis[u],u,tt.newnode());
}
inline void file(){freopen("conquer.in","r",stdin);freopen("conquer.out","w",stdout);}
int main(){
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int x,y,z,i=1;i<n;i++){
		x=read(),y=read(),z=read();
		add(x,y,z);add(y,x,z);
	}
	dfs(rt);dfs2(rt);
	ll ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,t[i]);
	cout<<ans;
	return 0;
}
©️2020 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值