[冲刺国赛2022] 模拟赛8

没有 \(\tt T2\),因为考试的时候就比较会,而且有点麻烦,不打算写了。

益智游戏

题目描述

\(n\leq 2000\)

解法

直接做并不容易,但是感觉本题应该有很好的性质,可以来分析一下。

首先忽略行之间的限制,只考虑单独一行的限制,发现有很好的分段结构,即 (22..2)-(11..1/33..3)-(44..4)

现在再把行之间的限制加上去,首先上下相邻的 \(1,3\) 不能 \(3\) 在上 \(1\) 在下;而 \(1\) 不能在 \(2,4\) 的下面;\(3\) 不能在 \(2,4\) 的上面;最后 \(2,4\) 也不能上下相邻;那么全局也会呈现一个很好的结构:

如图,全局分为上下两个部分,上面部分 \(1\) 的范围是越来越小的,下面部分 \(3\) 的范围是越来越大的。中间 \(1,3\) 的接壤处,必须要满足两个点的范围有交(特别地,如果有一方为空也视为有交),要不然会有 \(2,4\) 的限制。

拆解这个图的关键就是枚举这个交点,以交点划开整张图被分成了四部分。每一部分的最优解就是一个可以预处理 \(1,2,3,4\) 的前缀和,然后 \(dp\) 轮廓线。总时间复杂度 \(O(n^2)\)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 2005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,ans,x[M][M],y[M][M];
int a[M][M],b[M][M],c[M][M],d[M][M];
int f1[M][M],f2[M][M],f3[M][M],f4[M][M];
signed main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n=read();ans=1e18;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			x[i][j]=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			y[i][j]=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			a[i][j]=a[i-1][j];
			if(x[i][j]!=1) a[i][j]+=y[i][j];
			d[i][j]=d[i][j-1];
			if(x[i][j]!=4) d[i][j]+=y[i][j];
		}
	for(int i=n;i>=1;i--)
		for(int j=n;j>=1;j--)
		{
			c[i][j]=c[i+1][j];
			if(x[i][j]!=3) c[i][j]+=y[i][j];
			b[i][j]=b[i][j+1];
			if(x[i][j]!=2) b[i][j]+=y[i][j];
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)//14
			f1[i][j]=min(f1[i][j-1]+a[i][j],f1[i-1][j]+d[i][j]);
	for(int i=1;i<=n;i++)
		for(int j=n;j>=1;j--)//12
			f2[i][j]=min(f2[i][j+1]+a[i][j],f2[i-1][j]+b[i][j]);
	for(int i=n;i>=1;i--)
		for(int j=1;j<=n;j++)//34
			f3[i][j]=min(f3[i][j-1]+c[i][j],f3[i+1][j]+d[i][j]);
	for(int i=n;i>=1;i--)
		for(int j=n;j>=1;j--)//32
			f4[i][j]=min(f4[i][j+1]+c[i][j],f4[i+1][j]+b[i][j]);
	for(int i=0;i<=n;i++)
		for(int j=0;j<=n;j++)
			ans=min(ans,f1[i][j]+f2[i][j+1]+f3[i+1][j]+f4[i+1][j+1]);
	printf("%lld\n",ans);
}

区间距离

题目描述

有两个长度为 \(n\) 的序列 \(a_1,a_2...a_n\)\(b_1,b_2...b_n\)

\(m\) 次询问,每次给定 \(p_1,p_2,x\),询问下式的值:

\[\sum_{i=1}^x|a_{p_1+i-1}-b_{p_2+i-1}| \]

\(n\leq 10^5,m\leq 10^6,1\leq a_i,b_i\leq 5\)

解法

突破本题的关键点肯定是值域,而这种绝对值的和式有一个很好的微元贡献法(我已经是第三次见到了,但不明白为什么我做这题没有任何优势,可能是没时间仔细想了)

我们枚举 \(k\in[1,4]\),把 \(\leq k\) 的数标记为 \(1\),把 \(>k\) 的数标记为 \(0\),那么在询问时,我们把对应位置的数异或起来,那么异或值就是对答案的贡献。

如果要暴力地优化这个暴力算法,可以考虑拆位。我们每 \(64\) 位压缩成一个 unsigned long long,预处理出以 \(0\sim 63\) 为起点的压位结果。这样计算的时候就可以暴力遍历,由于我们预处理了模 \(64\) 所有余数的压位结果,那么两边就是一定对应得上的。

时间复杂度 \(O(nw+\frac{nm}{w})\)

#pragma GCC target("popcnt")
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 100005;
const int N = 1000005;
#define ull unsigned long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,a[M],b[M],p[N],s[N],t[N],ans[N];
int f[M],g[M];ull F[64][2000],G[64][2000];
void solve()
{
	for(int i=0;i<64;i++) for(int j=i;j<n;j+=64)
		for(int k=0;k<64;k++)
		{
			F[i][j>>6]|=((ull)f[j+k])<<k;
			G[i][j>>6]|=((ull)g[j+k])<<k;
		}
	for(int i=1;i<=m;i++)
	{
		ull *fp=F[s[i]&63]+(s[i]>>6);
		ull *gp=G[t[i]&63]+(t[i]>>6);
		int r=0,u=0;
		for(;u+512<=p[i];u+=512,fp+=8,gp+=8)
		{
			#define g(x) __builtin_popcountll(x)
			r+=g(fp[0]^gp[0]);
			r+=g(fp[1]^gp[1]);
			r+=g(fp[2]^gp[2]);
			r+=g(fp[3]^gp[3]);
			r+=g(fp[4]^gp[4]);
			r+=g(fp[5]^gp[5]);
			r+=g(fp[6]^gp[6]);
			r+=g(fp[7]^gp[7]);
			#undef g
		}
		for(;u<p[i];u++) r+=f[s[i]+u]^g[t[i]+u];
		ans[i]+=r;
	}
}
signed main()
{
	freopen("dist.in","r",stdin);
	freopen("dist.out","w",stdout);
	n=read();m=read();
	for(int i=0;i<n;i++) a[i]=read();
	for(int i=0;i<n;i++) b[i]=read();
	for(int i=1;i<=m;i++)
		s[i]=read()-1,t[i]=read()-1,p[i]=read();
	for(int w=1;w<=4;w++)
	{
		for(int i=0;i<n;i++)
			f[i]=(a[i]<=w),g[i]=(b[i]<=w);
		solve();
	}
	for(int i=1;i<=m;i++)
		write(ans[i]),puts("");
}
posted @ 2022-06-20 21:36  C202044zxy  阅读(158)  评论(0编辑  收藏  举报