The 14th Chinese Northeast Collegiate Programming Contest 补题(A.异或二进制位最小生成树 K.二维单调队列 L.二分+最大n维曼哈顿距离)

A.Micro Structure Thread(异或二进制位最小生成树)

题意比较迷惑,最后转化下来是,

确定一个树的点与父亲的排列,使得所求式总代价最小,

即求一棵最小生成树,点i和点j连接的代价是popcount(a[i]^a[j])

即ai和aj异或的值的二进制位的个数,

其中n<=2e5,0<=ai<2^18,ai两两不同

 

考虑将n个出现的值ai,把i视为祖先,多源bfs

对于每个[0,2^18)的值确定其祖先,然后考虑枚举扰动边,

枚举j从0到18,两个popcount之差1的相邻点,

如果祖先不同,就将这两个祖先连一条边,

考虑到a变到b的变化过程,

一定是从a,变到最远的离a最近的点,再变一次变到最远的离b最近的点,再变到b的

最后边的数量是(2^18)*18级别的,作Kruskal最小生成树即可

 

求出最小生成树后建树,dfs一遍即可确定点和父亲的对应关系的排列

由于根节点没有父亲,在父亲对应的排列里给父亲赋一个[1,n]的值即可

#include
using namespace std;
#define pb push_back 
typedef pair P; 
const int N=2e5+10,M=(1<<18)+5,INF=0x3f3f3f3f;
int t,n,m,a[N],bit[M],dis[M],par[M],fa[M];
int b[N],p[N],c;
bool vis[M];
vectore[N];
queueq;
struct edge{
	int u,v,w;
}g[M*18];
bool operator<(edge a,edge b){
	return a.w>1]+(i&1);
	}
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		c=m=0;
		for(int i=0;i

K.PepperLa's Boast(二维单调队列)

n*m(n<=1e3,m<=1e3,sum n*m<=7e6)的矩形,

点(i,j)上有一个值a(i,j)(-1e9<=a(i,j)<=1e9),表示这个点可以提供的氧气量,

如果为正,代表安全地区,

人的肺内氧气量在这里会加a(i,j),这里认为肺的容氧量是无穷的

如果为负数,则表示这个点不能提供氧气量,为毒气地区,

一个人想从(1,1)走到(n,m),保证a(1,1)>0,a(n,m)>0

每一秒只能向右或向下或向右下走一格,

即可以从(x,y)走到(x,y+1)/(x+1,y)/(x+1,y+1),

特别地,一个人可以选择在安全地区憋气,并耗掉u(u<=1e9)的代价,

憋气时间可以长达k(k<=1e9)秒,

在憋气时间内可以穿越毒气地区,并最终在安全地区松气,

求能安全到达(n,m)所剩下的最大氧气量

 

考虑到某个点的dp值需要从左上角长度为k的正方形转移而来,

于是需要用到二维单调队列,

赛中夏老师想出二维单调队列的想法的时候,

距比赛结束还有18min,于是写不完了gg

而且这个二维单调队列不是静态的,需要一边扫一边维护

静态的则可以先横着扫一遍扫完再竖着扫一遍扫完

 

首先由于氧气量是1e9级别的,就放在dp数组里进行维护,

dp[i][j]表示到(i,j)这个点能剩余的最大氧气量,初始化-1e18

ans[i][j]维护[dp[i][j-k],dp[i][j]]这一段长度为k的滑动窗口的最大dp值

 

分三种情况讨论,

第一种是左上角的格子,dp[1][1]=a[1][1]

第二种是大于0的格子,这个时候可以更新这个点的dp值,

dp值可以直接从dp[i-1][j-1]、dp[i-1][j]、dp[i][j-1]三个正dp值处直接转移而来

也可以左上角一个k*k的正方形处转移而来,

即横向的ans[i][j],和纵向的单调队列维护的窗口为k的ans队列

注意先用不带dp[i][j]的ans[i][j]更新dp[i][j],再用dp[i][j]更新ans[i][j]

第三种是小于0的格子,

这个时候仍然需要维护这个点左边长为k的区间的最大dp值,即ans[i][j]

样例很良心,本来一开始脑补的时候,忽略了第三种情况

#include
using namespace std;
typedef long long ll;
const int N=1e3+10;
int n,m,k,u,a[N][N];
ll dp[N][N],ans[N][N];
dequerow[N],col[N];
int main(){
	while(~scanf("%d%d%d%d",&n,&m,&k,&u)){
		for(int i=1;i<=n;++i){
			dp[i][0]=-1e18;
			while(!row[i].empty())row[i].pop_front();
		}
		for(int j=1;j<=m;++j){
			dp[0][j]=-1e18;
			while(!col[j].empty())col[j].pop_front();
		}
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				scanf("%d",&a[i][j]);
				dp[i][j]=-1e18;ans[i][j]=-1e18;
				while(!row[i].empty() && row[i].front()0){
					if(!row[i].empty()){
						dp[i][j]=max(dp[i][j],dp[i][row[i].front()]-u+a[i][j]);
						ans[i][j]=max(ans[i][j],dp[i][row[i].front()]);
					}
					if(!col[j].empty()){
						dp[i][j]=max(dp[i][j],ans[col[j].front()][j]-u+a[i][j]);
					}
					dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i][j]);
					dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i][j]);
					dp[i][j]=max(dp[i][j],dp[i][j-1]+a[i][j]);
					ans[i][j]=max(ans[i][j],dp[i][j]);
				}
				else{
					if(!row[i].empty()){
						ans[i][j]=max(ans[i][j],dp[i][row[i].front()]);
					}
				}
				if(dp[i][j]>=u){
					while(!row[i].empty() && dp[i][j]>=dp[i][row[i].back()])row[i].pop_back();
					row[i].push_back(j);
				}
				if(ans[i][j]>=u){
					while(!col[j].empty() && ans[i][j]>=ans[col[j].back()][j])col[j].pop_back();
					col[j].push_back(i);
				}
				//printf("i:%d j:%d dp:%lld\n",i,j,dp[i][j]);
			}
		}
		printf("%lld\n",dp[n][m]>0?dp[n][m]:-1);
	}
	return 0;
} 

L. PepperLa's Express(二分+最大三维曼哈顿距离)

z*x*y(z<=100,x<=100,y<=100),多组样例,sum z*x*y<=6e6,

一个三维的结构,每一层是一个矩阵,矩阵里有三种字符,@.*,

@代表邮局,.代表空地,*代表用户,每个用户会选择到自己家最近的邮局

距离定义为曼哈顿距离,

现在需要确定一个新邮局,建在空地上,

使得每个用户到最近的邮局的最大距离最小,输出最大距离

保证输入里至少有一个邮局和一个空地,

 

最大距离最小,考虑二分答案距离x,然后对邮局进行多源bfs,

找到所有用户,忽略到邮局距离不超过x的用户,然后考虑剩下的用户,

问题转化成,是否存在一个空地,对给定的用户的曼哈顿距离均不超过x

即最大n维曼哈顿距离问题

 

最大n维曼哈顿距离是一个经典问题,可以参考poj2926

利用了dp的松弛思想,即abs(x0-x1)=max(x0-x1,x1-x0)

abs(x0-x1)+abs(y0-y1)=max(x0-x1,x1-x0)+max(y0-y1,y1-y0)

=max(x0-x1+y0-y1,x0-x1+y1-y0,x1-x0+y0-y1,x1-x0-y1-y0)

=max(x0+y0-(x1+y1),x0-y0-(x1-y1),-x0+y0-(-x1+y1),-x0-y0-(-x1-y1)

三维同理,是八个值的最大值,

且如果枚举的(x0,y0)的当前符号固定,减去的值的符号(x1,y1)也与其相同

所以考虑所有可能的(x1,y1),用数组维护八维(x1,y1)的最小值,

对于所有的(x0,y0)插进去询问即可

 

这里,(x1,y1)先插入所有当前剩下的邮局,(x0,y0)则枚举所有的空地,

可以将sum=6e6认为是1e6的样例6组,

复杂度O(6*1e6*log300*8),实际跑不满,2s跑了1.3s

 

plus:如果是最小曼哈顿距离的话,则需用到三维树状数组,

这个时候偏序关系就必须发挥作用了,维护八棵树,分别询问,

可以参考2019年牛客多校第八场D.distance

#include
using namespace std;
const int N=105,INF=0x3f3f3f3f;
int z,x,y,dis[N][N][N],mn[8];
struct node{
	int a,b,c;
};
vectorsta,usr,spa;
char s[N][N][N];
queueq;
bool leg(int a,int b,int c){
	return a>=0 && a=0 && b=0 && cmid && s[a][b][c]=='*'){
			usr.push_back({a,b,c});
		}
		if(leg(a+1,b,c)){
			dis[a+1][b][c]=dis[a][b][c]+1;
			q.push({a+1,b,c});
		}
		if(leg(a-1,b,c)){
			dis[a-1][b][c]=dis[a][b][c]+1;
			q.push({a-1,b,c});
		}
		if(leg(a,b+1,c)){
			dis[a][b+1][c]=dis[a][b][c]+1;
			q.push({a,b+1,c});
		}
		if(leg(a,b-1,c)){
			dis[a][b-1][c]=dis[a][b][c]+1;
			q.push({a,b-1,c});
		}
		if(leg(a,b,c+1)){
			dis[a][b][c+1]=dis[a][b][c]+1;
			q.push({a,b,c+1});
		}
		if(leg(a,b,c-1)){
			dis[a][b][c-1]=dis[a][b][c]+1;
			q.push({a,b,c-1});
		}
	}
	if(!usr.size()){
		return 1;
	}
	memset(mn,INF,sizeof mn);
	for(int i=0;i

总结:

在队友的carry下9/13,以为很勇,

赛后被放到gym重现了,还删了一个签到题,这样就是8/12,

然而,神仙群友纷纷9/12,10/12,11/12异常神勇,radewoosh甚至单人3.5hAKorz

F题好像是一个O(n^5)的直接模拟,不想补了gg

你可能感兴趣的:(线上比赛,#,单调栈/单调队列,#,异或popcount最小生成树,二维单调队列,最大n维曼哈顿距离,第14届东北赛,线上比赛)