Codeforces Round #619

D. Time to Run(1s 256Mb)

题目大意

如图,n*m矩阵都存在这样(4nm-2n-2m)条有向边。要求给定一个数k,问能否从左上角的方块开始,不重复走k条边。如果可以给出路径。输出路径要求最多3000条指令,每条指令为一个数f和不超过4个字符的字符串,由\left \{ 'U','D','L','R' \right \}组成,表示按字符串走f次。输出时先输出指令数,再每行输出每条指令。

1 \leq n,m \leq500, 1 \leq k \leq 10^9

 

分析

只要k \leq (4nm-2n-2m) 就是YES,否则是NO。走的路径很容易看出,比如最简单的一条是

  1. 往右走 R
  2. 如果是第一行就往左走 L,否则上下左UDL
  3. 如果不是最后一行就往下D,否则向上到头U

关键是怎么输出,包括在终点时指令的截断。果然构造题别人的代码总是这么优美。思路是先把走到头的路构造出来,在删去多余的指令,避免分类讨论。

代码

int n, m, k;
vector<pair<int, string> > v, v2;

void fix()
{
	v2 = v;
	v.clear();
	for(pair<int, string> x : v2)
	{
		if(x.first) v.push_back(x);
	}
	return;
}

int main()
{
	n = read(), m = read(), k = read();
	int all = 0;
	for(int i = 1; i <= n; i++)
	{
		v.push_back({m-1, "R"});
		all += v.back().first * (int) v.back().second.size();

		if(i == 1) v.push_back({m-1, "L"});
		else v.push_back({m-1, "UDL"});
		all += v.back().first * (int) v.back().second.size();

		if(i == n) v.push_back({n-1, "U"});
		else v.push_back({1, "D"});
		all += v.back().first * (int) v.back().second.size();
	}
	
	if(all < k)
	{
		printf("NO\n");
		return 0;
	}
	
	while(all > k)
	{
		string tmp = v.back().second;
		all -= v.back().first * (int) v.back().second.size();
		v.pop_back();
		if(all < k)
		{
			int cha = k-all, len = tmp.length();
			v.push_back({cha/len, tmp});
			if(cha%len)
			{
				tmp.resize(cha%len);
				v.push_back({1, tmp});
			}
			break;
		}
	}
	
	fix();
	printf("YES\n%d\n", v.size()); 
	for(pair<int, string> x : v) cout << x.first << " " << x.second << endl;
	return 0;
}

E. Nanosoft(2s 512Mb)

题目大意

给定n*m矩阵,每个方块是\left \{ 'R','G','Y','B' \right \}中的一种颜色。给定q个询问,每个询问为(sx, sy)(ex, ey)子区域内最大的形如图的logo正方形面积是多少。

1 \leq n,m \leq500, 1 \leq q \leq 3*10^5

 

分析

可惜死a!比赛时前面的因为做过类似的题所以很快想出用四个方向二维前缀和来求出以每个点为中心得到的logo面积最大是多少,套用二维ST表,不过被询问是求子区域内被卡住了,因为区域可能会将最大logo截断,不同位置截断长度还不一样。

如果确定答案长度就可以在安全截断长度内寻找答案,所以使用二分答案,然后在合法区域内查找ST表,如果最大值大于mid则可以。

so close!!

O(n*m+log_n*log_m*n*m+log_{min(n, m)}*1)

代码

void init()
{
	for(int i = 1; i <= n; i++, getchar())
	for(int j = 1; j <= m; j++) a[i][j]= getchar();
	
	for(int i = 1; i <= n; i++)
	for(int j = 1; j <= m; j++) if(a[i][j] == 'R')
	{
		re[i][j] = 1+min(re[i-1][j-1], min(re[i][j-1], re[i-1][j]));
	}
	for(int i = 1; i <= n; i++)
	for(int j = m; j; j--) if(a[i][j] == 'G')
	{
		gr[i][j] = 1+min(gr[i-1][j+1], min(gr[i][j+1], gr[i-1][j]));
	}
	for(int i = n; i; i--)
	for(int j = 1; j <= m; j++) if(a[i][j] == 'Y')
	{
		ye[i][j] = 1+min(ye[i+1][j-1], min(ye[i][j-1], ye[i+1][j]));
	}
	for(int i = n; i; i--)
	for(int j = m; j; j--) if(a[i][j] == 'B')
	{
		bl[i][j] = 1+min(bl[i+1][j+1], min(bl[i][j+1], bl[i+1][j]));
	}
	for(int i = 1; i < n; i++)
	for(int j = 1; j < m; j++) st[0][0][i][j] = min(min(re[i][j], gr[i][j+1]), min(ye[i+1][j], bl[i+1][j+1]));
	return;
}

void build_st()
{
	for(int i = 1; i <= n; i++)
	for(int kj = 1; (1<<kj) <= m; kj++)
	for(int j = 1; j+(1<<kj)-1 <= m; j++) 
        st[0][kj][i][j] = max(st[0][kj-1][i][j], st[0][kj-1][i][j+(1<<(kj-1))]);
	
	for(int kj = 0; (1<<kj) <= m; kj++)
	for(int ki = 1; (1<<ki) <= n; ki++)
	for(int i = 1; i+(1<<ki)-1 <= n; i++)
	for(int j = 1; j+(1<<kj)-1 <= m; j++) 
        st[ki][kj][i][j] = max(st[ki-1][kj][i][j], st[ki-1][kj][i+(1<<(ki-1))][j]);
	return;
}

int ask(int sx, int sy, int ex, int ey)
{
	int ki = lo[ex-sx+1], kj = lo[ey-sy+1];
	return max( max(st[ki][kj][sx][sy], st[ki][kj][ex-(1<<ki)+1][sy]),
				max(st[ki][kj][sx][ey-(1<<kj)+1], st[ki][kj][ex-(1<<ki)+1][ey-(1<<kj)+1]) );
}

int check(int mid, int sx, int sy, int ex, int ey)
{
	if(sx > ex || sy > ey) return 0;
	return ask(sx, sy, ex, ey) >= mid;
}

int main()
{
	pre();
	n = read(); m = read(); q = read();
	init();
	build_st();
	while(q--)
	{
		int sx = read(), sy = read(), ex = read(), ey = read();
		int l = 0, r = min(ex-sx+1, ey-sy+1);
		while(l != r)
		{
			int mid = l + ((r-l) >> 1) + 1;
			if(check(mid, sx+mid-1, sy+mid-1, ex-mid, ey-mid)) l = mid;
			else r = mid-1;
		}
		printf("%d\n", l*l*4);
	}
	return 0;
}

 

F. Super Jaber(5s 256Mb)

题目大意

又是n*m矩阵,这一次每个方块有一种颜色,颜色总数为k。在1s内,你可以从一个方块走到上下左右四个方块中的一个,也可以传送到和现在所在方块同颜色的任意一个方块。q组询问,求两点间的最短距离。

1 \leq n,m \leq1000, 1 \leq k \leq min(n*m, 40)

分析

作为一道2700的题感觉挺水的,不过我也没做出来...。简单分析,不传送的话就是曼哈顿距离,传送的话就是枚举颜色,起点、终点到任意此颜色的最短距离+1。然而这样只能跳一次颜色,其他就没想出来了。。

多源BFS,让多颜色跳跃存在在预处理的最短距离里面。注意BFS性质,同一个颜色只需一次传送,否则会超时,复杂度不对。具体看代码。

O(k*n*m+q*k)

代码

void work(int x)
{
	queue<pair<int, int> > q;
	memset(vis, 0, sizeof(vis));
	for(pair<int, int> pr : b[x])
	{
		d[x][pr.fr][pr.se] = 0;
		q.push({pr.fr, pr.se});
	}
	vis[x] = 1;
	while(!q.empty())
	{
		pair<int, int> now = q.front(); q.pop();
		int cx = a[now.fr][now.se];
        // 一个颜色只BFS拓展一次,否则走回头路
		if(!vis[cx]++) for(pair<int, int> v : b[cx]) if(v != now && d[x][v.fr][v.se] == -1)
		{
			d[x][v.fr][v.se] = d[x][now.fr][now.se]+1;
			q.push({v.fr, v.se});
		}
		for(int i = 0; i < 4; i++)
		{
			int nx = now.fr+dir[i][0], ny = now.se+dir[i][1];
			if(nx && ny && nx <= n && ny <= m && d[x][nx][ny] == -1)
			{
				d[x][nx][ny] = d[x][now.fr][now.se]+1;
				q.push({nx, ny});
			}
		}
	}
	return;
}

int main()
{
	n = read(); m = read(); k = read();
	for(int i = 1; i <= n; i++)
	for(int j = 1; j <= m; j++) b[a[i][j] = read()].push_back({i, j});
	
	memset(d, -1, sizeof(d));
	for(int i = 1; i <= k; i++) work(i);
	
	int q = read(); while(q--)
	{
		int sx = read(), sy = read(), ex = read(), ey = read();
		int ans = abs(ex-sx)+abs(ey-sy);
		for(int i = 1; i <= k; i++) ans = min(ans, d[i][sx][sy]+d[i][ex][ey]+1);
		printf("%d\n", ans);
	}
	return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章