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
");
if (x==1) printf("D
");
if (x==2) printf("L
");
if (x==3) printf("R
");
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;
}
}