hgoi#20191114

T1-宇宙魔方

有一个 \(n^3\) 的立方体,每次会给这个立方体的一层都加上 \(X\) 或整体转动这个立方体
现在给你若干次操作后的立方体,有一个格子上的值不知道,请你求出

解法

直接模拟回溯所有的操作
具体是每次找到一层的最小值
全部减去即可

ac代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
inline void read(int&x)
{
	x=0;int fh=1;char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
	for(;isdigit(c);c=getchar())x=x*10+c-'0';
	x*=fh;
}
inline void print(int x)
{
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
int n,minn,a[110][110][110];
int main()
{
	freopen("cube.in","r",stdin);
	freopen("cube.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				read(a[i][j][k]);
	for(int i=1;i<=n;i++)
	{
		minn=inf;
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				if(a[i][j][k]>=0)
					minn=min(minn,a[i][j][k]);
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				a[i][j][k]-=minn;
		minn=inf;
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				if(a[j][i][k]>=0)
					minn=min(minn,a[j][i][k]);
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				a[j][i][k]-=minn;
		minn=inf;
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				if(a[j][k][i]>=0)
					minn=min(minn,a[j][k][i]);
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				a[j][k][i]-=minn;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				if(a[i][j][k]<0)
					print(-a[i][j][k]-1);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2-战争

给你一个带权无向图,要求你每次走的边权都要严格递增
求最远能走的距离

解法

这个根本不用建图
\(f_{i,j}\) 表示 \(i\) 号点作为终点,上一个走的权值为 \(j\) 的最远距离
我们发现一条边只会更新两个点,所以把第二维压掉
具体见代码

ac代码

#include<bits/stdc++.h>
using namespace std;
inline void read(int&x)
{
	x=0;int fh=1;char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
	for(;isdigit(c);c=getchar())x=x*10+c-'0';
	x*=fh;
}
inline void print(int x)
{
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
struct node
{
	int x,y,s;
	void init(){read(x),read(y),read(s);}
	bool operator<(const node&a)const{return s<a.s;}
}e[50010];
queue<int>q;
int n,m,ans,f[50010],g[50010];
int main()
{
	freopen("war.in","r",stdin);
	freopen("war.out","w",stdout);
	read(n),read(m);
	for(int i=1;i<=m;i++)e[i].init();
	sort(e+1,e+m+1);
	int nw=1,ed=1;
	while(nw<=m)
	{
		while(ed<m&&e[ed].s==e[ed+1].s)ed++;
		while(nw<=ed)
		{
			g[e[nw].x]=max(g[e[nw].x],max(f[e[nw].x],f[e[nw].y]+1));
			g[e[nw].y]=max(g[e[nw].y],max(f[e[nw].y],f[e[nw].x]+1));
			q.push(e[nw].x),q.push(e[nw].y),nw++;
		}ed++;
		while(!q.empty())f[q.front()]=g[q.front()],q.pop();
	}
	for(int i=0;i<n;i++)ans=max(ans,f[i]);print(ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3-和平

给你一棵带边权的树,若干次询问
每次询问 \(A、B、c\) 表示从A出发,走到B,只能通过边权大于等于 \(c\) 的点
求最多能走到哪里

解法

先拆成两部分,先从 \(A\) 走到 \(LCA\) ,再从 \(LCA\) 走到 \(B\)
如果停下的地方在第一部分,用倍增非常好做
但是如果停下的地方在第二部分怎么办呢
我们可以二分求这条路径的断点,然后求出一段的最小值,看是否符合要求
具体实现见代码

ac代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define mid (l+r>>1)
#define pb push_back
#define N 100010
#define K 18
using namespace std;
inline void read(int&x)
{
	x=0;int fh=1;char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')fh=-1;
	for(;isdigit(c);c=getchar())x=x*10+c-'0';
	x*=fh;
}
inline void print(int x)
{
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
vector<int>e[N],w[N];
int n,m,x,y,z,d[N],f[N][K],v[N][K];
void dfs(int u,int fa)
{
	int sz=e[u].size();
	for(int i=0;i<sz;++i)if(e[u][i]!=fa)
		f[e[u][i]][0]=u,v[e[u][i]][0]=w[u][i],
		d[e[u][i]]=d[u]+1,dfs(e[u][i],u);
}
inline int lca(int x,int y)
{
	if(d[x]<d[y])swap(x,y);
	if(d[x]>d[y])
	{
		int g=d[x]-d[y];
		for(int i=0;i<K;++i)
			if(g&(1<<i))
				x=f[x][i];
	}
	if(x==y)return x;
	for(int i=K-1;i>=0;--i)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
inline int get(int u,int g)
{
	int ans=inf;
	for(int i=0;i<K;++i)
		if(g&(1<<i))
			ans=min(ans,v[u][i]),
			u=f[u][i];
	return ans;
}
//得到u向上g条边的最小值
inline int find(int u,int g)
{
	for(int i=0;i<K;++i)
		if(g&(1<<i))
			u=f[u][i];
	return u;
}
//寻找u向上g条边的点
inline int query(int st,int ed,int G)
{
	int LCA=lca(st,ed),da=d[st]-d[LCA],db=d[ed]-d[LCA];
	int l=0,r=da+db,ans,D=get(st,da);
	while(l<=r)
		if((mid<=da?get(st,mid):min(D,get(find(ed,da+db-mid),mid-da)))<G)r=mid-1;else ans=mid,l=mid+1;
	//二分这个断点,如果在第一部分,那么路径只有一段
	//如果在第二部分,分成整个第一部分和第二部分断点上方的位置求解
	if(ans>da)return find(ed,da+db-ans);else return find(st,ans);
}
int main()
{
	freopen("peace.in","r",stdin);
	freopen("peace.out","w",stdout);
	read(n),read(m);
	for(int i=1;i<n;i++)
		read(x),read(y),read(z),
		e[x].pb(y),w[x].pb(z),
		e[y].pb(x),w[y].pb(z);
	dfs(1,0);
	for(int j=1;j<K;++j)
		for(int i=1;i<=n;++i)
			f[i][j]=f[f[i][j-1]][j-1],
			v[i][j]=min(v[i][j-1],v[f[i][j-1]][j-1]);
	for(int i=1;i<=m;++i)
		read(x),read(y),read(z),
		print(query(x,y,z)),putchar('\n');
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2019-11-14 12:49  可耐滴小慕容  阅读(120)  评论(0编辑  收藏  举报