Codeforces Round 996 (div 2) / 2055
A. Two Frogs
难度(个人感觉)★☆☆☆☆
思考:
先观察终止状态。要么左边的人面临选择,两人在最左边两个格子;要么右边的人面临选择,两人在最右边两个格子。
对于一种初始状态,至多只能到达其中的一种终止状态,这是因为
这个值始终不变。而两种终止状态的这个值是不同的,由于最终距离为1,因此若最终值为 0,那么 Bob 失败,否则最终值为 1, Alice 失败。
因此另一个人不可能输,而他可以选择往对方的方向走,使得不会出现平局。
Code:
void get(){
  if(abs(a - b) % 2 == 0){
    win = 1;
  } else{
    win = 0;
  }
}
B. Crafting
难度(个人感觉)☆☆☆☆☆
思考:
注意到最多只能存在一个 \(i\) 使得 a[i] < b[i],接下来模拟即可。
code:
int cnt = 0, val = 0;
for(int i = 0; i < N; i++){
  if(a[i] < b[i]){
    cnt++;
    val = b[i] - a[i];
  }
}
if(cnt > 1){
  ok = 0;
  return;
} else if(cnt == 0){
  ok = 1;
  return;
}
ok = 1;
for(int i = 0; i < N; i++){
  if(a[i] >= b[i] && a[i] - b[i] < val) ok = 0;
}
C. The Trail
难度(个人感觉)★★☆☆☆
思考:
首先,\(x\) 一般只能取 0,这是因为当 \(n\) 不等于 \(m\) 时,\(x\) 取其他值都会导致总和不同。
限制有 \(n + m - 1\) 个(因为所有限制是线性相关的,所有行和为 0,前 \(m-1\) 列和为 0 可以推导出第 \(m\) 列和为 0 ),那么我们可以每次使得一个限制满足,之后不破坏这个性质即可
做法:
若从格子 \(a\) 到格子 \(b\) 的方向是 \(R\),那么我们我们给 \(a\) 赋值为当前 \(a\) 所在列和的相反数,使列和为 0;
若从格子 \(a\) 到格子 \(b\) 的方向是 \(D\),那么我们我们给 \(a\) 赋值为当前 \(a\) 所在行和的相反数,使行和为 0;
例子:
3 3
DRRD
0 2 3
0 0 0
3 1 0
第一个字符是 D,因此我们赋值格子 (0, 0) 为第一行和的相反数,即 -5,之后第一行没有格子了
第二个字符是 R,因此我们赋值格子 (1, 0) 为第一列和的相反数 2(注意(0,0)已经变成 -5)
第三个字符是 R,因此我们赋值格子 (1, 1) 为第二列和的相反数 -3
第四个字符是 D,因此我们赋值格子 (1, 2) 为第二行和的相反数 1
而最后将格子 (2, 2) 变成 -4,这会使得最后一行,最后一列的和同时变成 0
Code:
void get(){
  memset(col, 0, sizeof(col));
  memset(row, 0, sizeof(row));
  for(int i = 1; i <= N; i++){
    for(int j = 1; j <= M; j++){
      col[j] += a[i][j], row[i] += a[i][j];
    }
  }    
  int x = 1, y = 1;
  for(char c : path){
    if(c == 'R'){
      a[x][y] = -col[y];
      col[y] += a[x][y], row[x] += a[x][y];
      y++;
    } else{
      a[x][y] = -row[x];
      col[y] += a[x][y], row[x] += a[x][y];
      x++;
    }
  }
  a[x][y] = -row[x];
}
//注意 所有数组得开 longlong 
D. Scarecrow
难度(个人感觉)★★★☆☆
思考:
首先任意时刻,乌鸦左边的稻草人,只有最靠近的那个是有用的,显然它们的距离是 \(k\)。
因此,对于乌鸦第一次超过第\(i\)个稻草人的这些状态,可以用 右边第一个稻草人 \(i\),时间 \(t\) 和 乌鸦的位置 \(p\) 唯一确定。我们可以先不安排乌鸦右边所有稻草人的位置。
- 
显然对应同样的 \(p\),时间 \(t\) 越小越好,时间大的作用是让右边的稻草人移动,那么我们等待一些时间就行。
 - 
进一步的,对于同样的 \(t\),\(p\) 越大越好,因为我们之后可以执行一样的操作,乌鸦还是能到终点的。
 
万事俱备,贪心即可。假设乌鸦位置 \(p\),乌鸦右边第一个稻草人是 \(i\),时刻为 \(t\),那么这个稻草人的位置可以在 \([a[i]-t,a[i]+t]\) 之间。
对于其中一个位置 \(x\),对于 \(x \le p\),那么 \(x\) 越大越好,这是因为 2. 。
对于 \(x \ge p\),那么 \(x\) 越小越好,这是因为如果第 \(i-1\) 个稻草人在“接到”乌鸦之后还向右移动了 \(d\),可以选一个合适的 \(dx\),让 \(x\) 变小 \(dx\),\(d\) 减少 \(dx\),这样第 \(i\) 个稻草人还是能接到乌鸦。再将第 \(i\) 个稻草人向右移动 \(dx\),那么 \(p\) 不会变差。
整理一下,如果区间包含 \(p\),那么取 \(p\),否则取最接近 \(p\) 的数。
如果取的数 \(x \ge p\),那么我们让第 \(i-1\) 个稻草人右移(和2. 一样),第 \(i\) 个稻草人左移(左移的正确性和上面相同),花费时间 \((p - x) / 2\) 。否则可以不花费时间。
Code:
void input(){
  a[i] *= 2;//输出时间两倍,相当于距离全部变成两倍
}
void get(){ 
  int p = 0;
  t = a[0]; a[0] = 0; 
  for(int i = 0; i < N - 1; i++){
    if(a[i + 1] - a[i] < k){
      if(a[i + 1] + t >= a[i] + k){
        a[i + 1] = a[i] + k; 
      } else{
        a[i + 1] += t;
      }
    } else{
      if(a[i + 1] - t <= a[i] + k){
        a[i + 1] = a[i] + k;
      } else{
        int dt = ((a[i + 1] - t) - (a[i] + k)) / 2;
        t += dt;
        a[i + 1] = a[i] + dt + k;
      }
    }
  }
  if(a[N - 1] + k < L){
    t += L - (a[N - 1] + k);
  }
}
                    
                
                
            
        
浙公网安备 33010602011771号