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;
}

 

 

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