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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章