Codeforces Round #416 (Div. 2)

Problem C. Vladik and Memorable Trip

题目大意

有n个人打算坐火车,排成了一列。给定每个人要去的目标城市。将这些人分成若干段,同一段内的人坐在同一节车厢里面。(也可以不分配某个人,即这个人不坐火车)规定去往相同城市的人要么都不坐火车,要么都在火车的同一节车厢里面。定义每节车厢的舒适度为同一节车厢内去往城市的编号的异或和。询问所有车厢的舒适度之和的最大值。

解题分析

动态规划。
定义 dp[i] 表示前i个人坐火车的车厢的舒适度之和的最大值。枚举 j 为 0 ~ i - 1 , 使得 j + 1 ~ i 的人坐在同一车厢,若满足题目条件则更新答案。具体判断过程见程序。

参考程序

#include <bits/stdc++.h>
using namespace std;

const int N=5008;
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define repd(i,x,y) for (int i=x;i>=y;i--)

int a[N];
int n;
int dp[N];
int l[N],r[N],flag[N];

int main()
{
	cin>>n;
	rep(i,1,n) cin>>a[i];
	rep(i,1,n)
	{
		l[a[i]]=l[a[i]]==0?i:min(l[a[i]],i);
		r[a[i]]=r[a[i]]==0?i:max(r[a[i]],i);		
	}
	rep(i,1,n)
	{
		int limit=0;
		int sum=0;
		memset(flag,0,sizeof(flag));
		repd(j,i,1)
		{
			if (r[a[j]]>i) break;
			limit=limit==0?l[a[j]]:min(limit,l[a[j]]);
			if (limit<=j && !flag[a[j]]) 
			{
				flag[a[j]]=1;
				sum=sum ^ a[j];
			}
			if (limit==j)
				dp[i]=max(dp[i],dp[j-1]+sum);
		}
		repd(j,i,1) dp[i]=max(dp[i],dp[j]);
	}
	//rep(i,1,n) cout<<dp[i]<<" ";
	cout<<dp[n]<<endl;
}

Problem D. Vladik and Favorite Game

题目大意

交互式题目。
给一张地图。给出终点和障碍。起点默认为(1,1)。
每次输出一个方向,会得到一个当前所在位置的输入。
不过,上下和左右可能是颠倒的,即往上走可能得到一个实际往下走的位置。
要求给出一定的输出,使得最后走到终点。

解题分析

关键在于求出上下和左右是否被颠倒。
如果某个点上下两个方位都是可以走的,那么可以通过这个点判断出上下是否被颠倒。左右方向同理。
故只需先判断起点能否判断上下或左右(必然可以判断出一个),然后再那一条直线上找出另一个可以判断出另一个方向的点就行了。
最后bfs找出一条到达终点的路径。

参考程序

#include <bits/stdc++.h>
using namespace std;

#define rep(i,x,y) for (int i=x;i<=y;++i)
#define U 0
#define D 1
#define L 2
#define R 3
const int dx[4]={-1,1,0,0};
const int dy[4]={0,0,-1,1};
int a[200][200];
char s[200];
int tx,ty,m,n,p=-1,q=-1,X,Y;
void ptget(int x)
{
	if (x==0) printf("U\n");
	if (x==1) printf("D\n");
	if (x==2) printf("L\n");
	if (x==3) printf("R\n");
	fflush(stdout);
	scanf("%d%d",&X,&Y);
	if (X==tx && Y==ty) exit(0);
	if (X==-1 || Y==-1) exit(0);
}

bool move_lr(int x,int y)
{
	if (a[x][y-1]==-1) return 0;
	if (a[x][y+1]==-1) return 0;
	return 1;
}
bool move_ud(int x,int y)
{
	if (a[x-1][y]==-1) return 0;
	if (a[x+1][y]==-1) return 0;
	return 1;
}
struct node
{
	int x,y;
};
queue <node> Q;
int dis[200][200];
int main()
{
	cin.sync_with_stdio(0);
	cin>>m>>n;
	rep(i,1,m)
	{
		cin>>s+1;
		rep(j,1,n)
		{
			if (s[j]=='*') a[i][j]=-1; else a[i][j]=0;
			if (s[j]=='F') tx=i,ty=j;
		}
	}
	if (move_lr(1,1))
	{
		ptget(L);
		if (Y==1)
		{
			p=0;
		}
		else
		{
			p=1;
			ptget(L ^ p);
		}
		int way;
		rep(i,1,n) if (move_ud(1,i)) {way=i;break;}
		rep(i,1,way-1) ptget(R ^ p);
		ptget(U);
		if (X==1)
		{
			q=0;
		}
		else
		{
			q=1;
			//ptget(U ^ q);
		}
		//rep(i,1,way-1) ptget(L ^ p);
	}
	else
	{
		ptget(U);
		if (X==1)
		{
			q=0;
		}
		else
		{
			q=1;
			ptget(U ^ q);
		}
		int way;
		rep(i,1,m) if (move_lr(i,1)) {way=i;break;}
		rep(i,1,way-1) ptget(D ^ q);
		ptget(L);
		if (Y==1)
		{
			p=0;
		}
		else
		{
			p=1;
		}
	}
	dis[tx][ty]=1; Q.push((node){tx,ty});
	while (!Q.empty())
	{
		node xh = Q.front(); Q.pop();
		rep(i,0,3)
		{
			int x=xh.x+dx[i];
			int y=xh.y+dy[i];
			if (x>=1 && x<=m && y>=1 && y<=n && dis[x][y]==0 && a[x][y]==0)
			{
				dis[x][y]=dis[xh.x][xh.y]+1;
				Q.push((node){x,y});
			}
		}	
	}
	int nowx=X,nowy=Y;
	while (nowx!=tx || nowy!=ty)
	{
		int dir=-1;
		rep(i,0,3)
		{
			int x=nowx+dx[i];
			int y=nowy+dy[i];
			if (x>=1 && x<=m && y>=1 && y<=n && dis[x][y]==dis[nowx][nowy]-1)
			{
				dir=i;
				break;
			}
		}
		if (dir==0 || dir==1) ptget(dir ^ q); else ptget(dir ^ p);
		nowx = X; nowy = Y;
	}
}

Problem E Vladik and Entertaining Flags

题目大意

有一张m*n的矩阵,每个点上有一个数字,所有相邻的相同数字被称为一段。
有Q个询问,每次给出 l,r,询问在(1 * l ~ m * r)矩阵中有多少段数字。
1 <= m <= 10, 1 <= n, q <= 10 ^ 5

解题分析

由于m很小,考虑用线段树来做。
记录一下每段区间的左边和右边的颜色以及段落数,如果左边的颜色块和右边的颜色块是连通的话,那么用相同的数字来表示。
每次合并的时候用并查集来维护。

参考程序

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 8;
const int M = 20; 
struct node
{
	int sum;
	int a[M];
	int b[M];
}T[N << 2];

int a[M][N];
int f[N], id[N];

int n, m, q;

int find(int x)
{
	if (f[x] == x) return x;
	return f[x] = find(f[x]);
}

void merge(node &rt, node &l, node &r, int lpos, int rpos)
{
	rt.sum = l.sum + r.sum;
	for (int i = 0; i < m; ++i)
	{
		f[i] = l.a[i];
		f[i + m] = l.b[i];
		f[i + m * 2] = r.a[i] + m * 2;
		f[i + m * 3] = r.b[i] + m * 2;
	}
	for (int i = 0; i < m; ++i)
	{
		if (a[i][lpos] == a[i][rpos] && f[find(i +  m * 2)] != f[find(i + m)])
		{
			f[find(i +  m * 2)] = f[find(i + m)];
			rt.sum--;
		}
	}		
	for (int i = 0; i < m * 4; ++i) f[i] = find(f[i]), id[f[i]] = -1; 
	for (int i = 0; i < m; ++i)
	{
		if (id[f[i]] == -1) id[f[i]] = i;
		rt.a[i] = id[f[i]];
	}
	for (int i = m * 3; i < m * 4; ++i)
	{
		if (id[f[i]] == -1) id[f[i]] = i - m * 2;
		rt.b[i - m * 3] = id[f[i]];	
	}
}

void query(int L, int R, int l, int r, int rt, node &ans)
{
	if (L <= l && r <= R)
	{
		ans = T[rt];
		return;
	}
	int mid = l + r >> 1;
	if (R <= mid) {query(L, R, l, mid, rt << 1, ans); return;}
	if (mid < L) {query(L, R, mid + 1, r, rt << 1 | 1, ans); return;}
	node p, q;
	query(L, R, l, mid, rt << 1, p);
	query(L, R, mid + 1, r, rt << 1 | 1, q);
	merge(ans, p, q, mid, mid + 1);
}

void build(int l, int r, int rt)
{
	if (l == r)
	{
		T[rt].sum = 0;
		for (int i = 0; i < m; ++i) 
			if (i == 0 || a[i][l] != a[i - 1][l])
			{
				T[rt].a[i] = T[rt].b[i] = i;
				T[rt].sum++;
			}
			else
			{
				T[rt].a[i] = T[rt].b[i] = T[rt].a[i-1];
			}
		return;
	}
	int mid = l + r >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	merge(T[rt], T[rt << 1], T[rt << 1 | 1], mid, mid + 1);
}
int main()
{
	cin.sync_with_stdio(0);
	cin >> m >> n >> q;
	for (int i = 0; i < m; ++i)
		for (int j = 1; j <= n; ++j)
			cin >> a[i][j];
	build(1, n, 1);
	node ans;
	for (int i = 0; i < q; ++i)
	{
		int l, r;
		cin >> l >> r;
		query(l, r, 1, n, 1, ans);
		cout << ans.sum << endl;
	}
}
posted @ 2017-05-28 18:03  rpSebastian  阅读(282)  评论(0编辑  收藏  举报