zr2019暑期高端峰会AB组day4

本文深入解析了zr2019暑期高端峰会AB组day4的三道算法题目:猫、打地鼠和奇怪的和。详细介绍了每道题目的解题思路、关键算法和代码实现,特别关注了斜率优化DP、01压位加BSF搜索和大力推式子数学题的技巧。

zr2019暑期高端峰会AB组day4

A. 猫

link

  • 先给距离做个前缀和,知道每座山离一号位置的距离
  • 对于每只猫,求出 a [ i ] a[i] a[i]代表最晚在 a [ i ] a[i] a[i]时刻出发可以接到低 i i i只猫
  • 我们再按 a [ i ] a[i] a[i]给每只猫从小到大排序,就变成了把这些猫分成最多 p p p段的问题,每次分段有相应的代价
  • 方程是 d p [ i ] = min ⁡ { d p [ j ] + s [ i ] − s [ j ] + ( i − j ) ∗ a [ i ] } dp[i]=\min \{dp[j]+s[i]-s[j]+(i-j)*a[i]\} dp[i]=min{dp[j]+s[i]s[j]+(ij)a[i]}
  • s [ i ] − s [ j ] s[i]-s[j] s[i]s[j]这一项可以不看,最后直接加上全长,这个式子关于 i i i的和关于 j j j的乘到了一起,显然可以斜率优化DP
  • 考场上数据范围算错了没开 l o n g l o n g long long longlong爆了10分,90分

B. 打地鼠

link

  • 这题20分就是01压位加个BSF
  • 50分好像还是搜索,再加上一些特判??
  • 然而场上A了这题的四个人只有一个写了正解,其他的居然都是写了暴力对拍,把所有无解的情况都拍出来,大概四五种,全部判一下,否则输出1
  • 那么正解需要我们反着考虑,对于一个在下面的地鼠,我们考虑把它拔出来,我们令下去的地鼠是黑色,上来的是白色,那么一个可以拔出来的地鼠必需满足同行同列只有这一个黑色,那么这行这列的其他地鼠我们给它标记灰色,代表可黑可白,最后我们再看看这个形态是否符合初始形态
  • 由于我们每次做这个逆操作的时候都判断了这个地鼠是不是行列中唯一的黑点,所有我们只会把白点变成灰点,这些灰点又可以被当做黑点做逆操作
  • 最后有很多灰色的交点,必需有一个是白色的,不然没法起步,其他位置对应相等即可

C. 奇怪的和

link

  • 这是什么大力推式子数学题啊,赛后都只有两个人改出来,我是真的顶不住
  • 果断咕之
T1考场代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b);i--)
#define mem(i,j) memset(i,j,sizeof(i))
#define GO(u) for (register int j=f[u];j!=-1;j=nxt[j])
#define pb push_back
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int inf=1e9+7;
int n,m,p,d[N],h[N],t[N],ans;
int a[N],pre[N],s[N];
int f[2][N],cur=0,line[2][N],L[2],R[2];
int read()
{
	int x=0,f=1;
	char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return f*x;
}
void init()
{
	FOR(i,2,n) s[i]=s[i-1]+d[i];
	FOR(i,1,m) a[i]=t[i]-s[h[i]];
	sort(a+1,a+m+1);
	FOR(i,1,m) pre[i]=pre[i-1]+a[i];
	return;
}
double slope(int i,int j,int tag)
{
	double ret=1.0*(f[tag][j]-f[tag][i])/(1.0*(j-i));
	return ret;
}
void DP()
{
	line[cur^1][L[cur^1]=R[cur^1]=1]=0;
	FOR(round,1,p)
	{
		cur^=1;
		line[cur^1][L[cur^1]=R[cur^1]=1]=0;
		FOR(i,1,m)
		{
			while (L[cur]<R[cur]&&line[cur][L[cur]+1]<i&&slope(line[cur][L[cur]],line[cur][L[cur]+1],cur^1)<a[i]) L[cur]++;
			f[cur][i]=f[cur^1][line[cur][L[cur]]]-line[cur][L[cur]]*a[i]+i*a[i];
			while (L[cur^1]<R[cur^1]&&slope(line[cur^1][R[cur^1]-1],line[cur^1][R[cur^1]],cur)>slope(line[cur^1][R[cur^1]],i,cur)) R[cur^1]--;
			line[cur^1][++R[cur^1]]=i;
		}
	}
	ans=f[cur][m]-pre[m];
	return;
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("myans.out","w",stdout);
	n=read(),m=read(),p=read();
	FOR(i,2,n) d[i]=read();
	FOR(i,1,m) h[i]=read(),t[i]=read();
	init();
	DP();
//	Debug();
	printf("%d\n",ans);
	return 0;
}
T2赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
const int N=1010;
int n,m,G[N][N],vis[N][N],vis1[N],vis2[N],cntx[N],cnty[N];
char A[N][N],B[N][N],l=1,r=0,tag=0;
pii line[N];
int eq()
{
	FOR(i,1,n) FOR(j,1,m) if (A[i][j]!=B[i][j]) return 0;
	return 1;
}
int main()
{
	scanf("%d %d",&n,&m);
	FOR(i,1,n) scanf("%s",A[i]+1);
	FOR(i,1,n) scanf("%s",B[i]+1);
	if (eq()) {printf("1\n");exit(0);}
	FOR(i,1,n) FOR(j,1,m) if (B[i][j]=='X') cntx[i]++,cnty[j]++,G[i][j]=1;
	FOR(i,1,n) FOR(j,1,m) if (B[i][j]=='X'&&cntx[i]==1&&cnty[j]==1) line[++r]=mp(i,j),vis[i][j]=1;
	while (l<=r)
	{
		pii tmp=line[l++];
		if (vis1[tmp.fi]<=1)
		{
			vis1[tmp.fi]++;
			FOR(i,1,m) if (G[tmp.fi][i]==1) cntx[tmp.fi]--,cnty[i]--,G[tmp.fi][i]=2;
			FOR(i,1,m) if (cntx[tmp.fi]==0&&cnty[i]==0&&!vis[tmp.fi][i]) vis[tmp.fi][i]=1,line[++r]=mp(tmp.fi,i);
		}
		if (vis2[tmp.se]<=1)
		{
			vis2[tmp.se]++;
			FOR(i,1,n) if (G[i][tmp.se]==1) cntx[i]--,cnty[tmp.se]--,G[i][tmp.se]=2;
			FOR(i,1,n) if (cntx[i]==0&&cnty[tmp.se]==0&&!vis[i][tmp.se]) vis[i][tmp.se]=1,line[++r]=mp(i,tmp.se);
		}
	}
	FOR(i,1,n) if (vis1[i]) FOR(j,1,m) if (vis2[j]) if (A[i][j]=='O') tag=1;
	if (!tag) {printf("0\n");exit(0);}
	FOR(i,1,n) if (!vis1[i]) FOR(j,1,m) if (!vis2[j]) if (A[i][j]!=B[i][j]) {printf("0\n");exit(0);}
	printf("1\n");
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值