B Eezie and Pie
题意:给一颗树,1为根节点,树的每个节点都能向上走k步,问每个节点能被多少节点走到。
题解:树上差分,如果从6能向上继续走到3->4->1,则可在6的位置+1,在1的父节点位置-1,除此之外向上寻找能走到的最大父节点的时候需要用倍增实现
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int h[N], e[N << 1], ne[N << 1], idx;
int d[N], dept[N];
int fa[N][30];//从i走2^j步能到达的节点
int cnt[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void bfs()
{
queue<int> q;
q.push(1);
memset(dept, 0x3f, sizeof dept);
dept[0] = 0;
dept[1] = 1;
while(q.size())
{
int t = q.front(); q.pop();
for(int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if(dept[j] > dept[t] + 1)
{
dept[j] = dept[t] + 1;
fa[j][0] = t;
q.push(j);
for(int k = 1; k <= 28; k ++ )
fa[j][k] = fa[fa[j][k - 1]][k - 1];
//只能向上走,而且上边的路径是完整的,所以可以边bfs边找父节点
}
}
}
}
int findp(int u)
{//寻找父节点
int len = d[u];
for(int i = 28; i >= 0; i -- )
if(len >= (1 << i))
len -= (1 << i), u = fa[u][i];
return u;
}
void dfs(int u, int f)
{
cnt[u] ++;
int p = findp(u);
cnt[fa[p][0]] --;
for(int i = h[u]; ~i; i = ne[i])
{
int t = e[i];
if(t == f) continue;
dfs(t, u);
cnt[u] += cnt[t];
//差分数组相加,dfs之后u的子树t已经全部处理完毕,直接相加就行
}
}
int main()
{
int n; cin >> n;
memset(h, -1, sizeof h);
for(int i = 1; i < n; i ++ ){
int a, b; cin >> a >> b;
add(a, b);add(b, a);
}
for(int i = 1; i <= n; i ++ ) cin >> d[i];
bfs();
dfs(1, 0);
for(int i = 1; i <= n; i ++ ) cout << cnt[i] << ' ';
return 0;
}
M Z-Game on grid
题意:A和B走一盘棋,每次只能走到下方或者右方,A先走。A如果走到位置有‘A’的地方则A获胜,B如果走到有‘B’的地方则B获胜,如果走到(n,m)的位置还是‘.’则为平局,问B随便走,A是否能一定获胜,平局,输
题解:dp,三维状态,分别表示为从(i,j,0)出发是否能走到A,从(i,j,1)出发是否能走到B,从(i,j,2)出发是否能走到C。
横坐标+纵坐标为偶数则表示A从当前点走,否则B走。
以能否走到A为例,(i,j)的状态一定是从(i + 1, j)和(i,j + 1)转移过来,只要有一个方向能获胜则一定可以获胜,因为决定权在A手中。B就不一样了,无论B向哪个方向走,一定得赢或者输或者平局,只有一种结构,决定权实际还是在A手中。
注意:还需要处理边界,因为有&&如果现在的位置处于边界的位置,比如在第n行,只能向右走,但B需要判断下和右是不是同一个值,如果是才能置为true
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
bool f[N][N][3];
//从(i,j,0)出发是否能走到A,从(i,j,1)出发是否能走到B,从(i,j,2)出发是否能走到C
char g[N][N];
int main()
{
int t; cin >> t;
while(t -- )
{
int n, m; cin >> n >> m;
memset(f, 0, sizeof f);
for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ ){
cin >> g[i][j];
if(g[i][j] == 'A') f[i][j][0] = true;
else if(g[i][j] == 'B') f[i][j][1] = true;
}
if(g[n][m] == '.') f[n][m][2] = true;
for(int i = n; i >= 1; i -- ){
for(int j = m; j >= 1; j -- ){
if(i == n && j == m) continue;
int st = (i + j) & 1;
if(i == n){
f[n + 1][j][0] = f[n][j + 1][0];
f[n + 1][j][1] = f[n][j + 1][1];
f[n + 1][j][2] = f[n][j + 1][2];
}
if(j == m){
f[i][m + 1][0] = f[i + 1][m][0];
f[i][m + 1][1] = f[i + 1][m][1];
f[i][m + 1][2] = f[i + 1][m][2];
}
if(!st){//和为偶数,A开始走
if(g[i][j] == '.'){
f[i][j][0] = max(f[i][j][0], f[i + 1][j][0] || f[i][j + 1][0]);
f[i][j][1] = max(f[i][j][1], f[i + 1][j][1] || f[i][j + 1][1]);
f[i][j][2] = max(f[i][j][2], f[i + 1][j][2] || f[i][j + 1][2]);
}
}
else {
if(g[i][j] == '.'){
f[i][j][0] = max(f[i][j][0], f[i + 1][j][0] && f[i][j + 1][0]);
f[i][j][1] = max(f[i][j][1], f[i + 1][j][1] && f[i][j + 1][1]);
f[i][j][2] = max(f[i][j][2], f[i + 1][j][2] && f[i][j + 1][2]);
}//可以不用max,因为每个位置只走过一次
}
}
}
if(f[1][1][0]) cout<<"yes";
else cout << "no";
if(f[1][1][2]) cout << " yes";
else cout<<" no";
if(f[1][1][1]) cout << " yes";
else cout << " no";
cout<< endl;
}
return 0;
}