Educational Codeforces Round 138 (Rated for Div. 2)


一、A. Cowardly Rooks

  • 思路: 从题目可以得出,m <= n,因为前面都给排好了,所以如果m == n,不管移动哪一个都不行,反之如果m < n,就移动某一行或者某一列为空的相邻行和列中的物品
  • 代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define int long long
#define pb push_back
#define endl '\n'
#define all(x) x.begin(),x.end()
#define PII pair<int,int>

using namespace std;

const int N = 2e5 + 100,mod=998244353;
bool st[10][10];
void solve()
{
     int n,m; cin >> n >> m;
 
     for(int i = 1;i <= m;i ++ )
     {
         int x,y; cin >> x >> y;
     }
     if(m >= n) cout << "NO" << endl;
     else cout << "YES" << endl;
     
}

signed main() 
{
    ios::sync_with_stdio(false); cin.tie(0);
    int T; cin >> T;
    while(T -- ) solve();

}

二、B - Death’s Blessing

  • 思路: 因为A数组中的所有元素肯定要加上去,我们遍历一边数组发现,不管从哪一个开始,只会留一个b[i],没有被便利,所以贪心来看,就让b[i]最大的最后便利,排个序就行
  • 代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define int long long
#define pb push_back
#define endl '\n'
#define all(x) x.begin(),x.end()
#define PII pair<int,int>

using namespace std;

const int N = 2e5 + 100,mod=998244353;
bool st[10][10];
void solve()
{
     int n,m; cin >> n;
     int sum = 0;
     for(int i = 1;i <= n;i ++ )
     {
         int x;
         cin >> x;
         sum += x;
     }
     int mx = -1;
     for(int i = 1;i <= n;i ++ ) 
     {
         int x; cin >> x;
         mx = max(mx,x);
         sum += x;
     }
     
      cout << sum - mx << endl;
}

signed main() 
{
    ios::sync_with_stdio(false); cin.tie(0);
    int T; cin >> T;
    while(T -- ) solve();

}

三、C - Number Game

思路: A操作越多,能够选择的数字越少,所以贪心来看,就让A尽量选择大的删除,同时B为了不让A操作过多,贪心来看,就尽量在元素值小的地方加上去,这样给a数组排序,因为k最大是(n + 1) / 2,n <= 100,所以直接暴力枚举k的取值,当然二分也行,然后看看在当前的k下,是否满足条件
代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define int long long
#define pb push_back
#define endl '\n'
#define all(x) x.begin(),x.end()
#define PII pair<int,int>

using namespace std;

const int N = 2e5 + 100,mod=998244353;
int a[110];

void solve()
{
     int n,m; cin >> n;
     int d = 0; // k最大是n + 1 / 2;
     for(int i = 1;i <= n;i ++ ) cin >> a[i];
     sort(a + 1,a + 1 + n);
     
     for(int k = (n + 1) / 2;k >= 1;k -- )
     {
         int u = k;
         int d = 0;
         int i = 1,j = n;
         
         while(i <= j)
         {
             while(j >= i && a[j] > u) j--;
             if(j >= i && a[j] <= u)
             {
                 d++;
                 j--;
             }
             else
             break;
             
             u--;
             i++;
         }
         
         
         if(d >= k) 
         {
             cout << k << endl;
             return;
         }
         
     }
     
     cout << "0" << endl;
     //如果数组中没有1,那就是0
     // 如果有1个1,答案就是1
     // 如果有两个1,答案就是2
}

signed main() 
{
    ios::sync_with_stdio(false); cin.tie(0);
    int T; cin >> T;
    while(T -- ) solve();

}

四、D - Counting Arrays

  • 思路: 我们发现计算符合的条件个数太多,于是我们可以计算不符合的条件个数,然后让总数 - 不符合的条件个数,不管什么排列,最少都有一次,[1,1,1,1,1,1…],如果不想这个排列有至少两次,
    对于a[2],那就必须符合gcd(a[2],2) != 1,
    对于a[3],就必须符合gcd(a[3],3) != 1,并且gcd(a[3],2) != 1,也就是说a[3] 必须得是 2和3的倍数
    对于a[4], 就必须符合gcd(a[4],4) != 1,并且gcd(a[4],3) != 1并且gcd(a[4],2) != 1,a[4],也必须是2,3,4的倍数,但是4已经是2的倍数,所以就是质数的倍数,就行
    对于a[n],就必须符合gcd(a[n],n) != 1,并且gcd(a[n],n - 1) != 1…gcd(a[n],2) != 1
    对于a[i],就必须让i这个位置上面的数字是i的倍数,同时也是i - 1的倍数…同时也是2的倍数,那我们就用他们的质因数来求解就行
    观察这个规律发现,在 i 对于 i 这个位置,如果 i 是 合数,那不用动,如果 i 是质数,那就必须使得 tmp = tmp * i,那在这个位置不能选的数字数量是 m / tmp,用w来存当前长度为 i 不能选的排列数量 那对于当前长度为 i 且不能选的排列数就是 w = w * (m ./ tmp),最后求一遍全部的排列数量 减去不能选的排列个数即可
  • 代码:
#include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>

using namespace std;
const int N = 2e5 + 100,mod = 998244353;
int p[N],cnt;
bool st[N];

void init()
{
    for(int i = 2;i <= N;i ++ )
    {
        if(!st[i])
        p[cnt++] = i;
        
        for(int j = 0;p[j] <= N / i;j ++ )
        {
            st[i * p[j]] = true;
            if(i % p[j] == 0) break;
        }
        
    }
}

int quick(int a,int b)
{
    int k = 1;
    a %= mod;
    while(b)
    {
        if(b & 1)
        {
            k = k * a % mod;
        }
        
        b = b >> 1;
        a = a * a % mod;
    }
    
    return k;
}
void solve() 
{
   int n,m; cin >> n >> m;
    
  int tmp = 1;
  int w = m % mod,res =0;
  int y = m % mod;
   for(int i = 2;i <= n;i ++ ) // 要知道每一个a[i],有多少种选法使得
   {
       if(!st[i] && tmp <= m) // 质数
       tmp = tmp * i;
     
     
       w = (w * ((m / tmp) % mod)) % mod; // 不超过1e12
       res = (res + w) % mod; 
   }
   int ans = 0;
   for(int i = 2;i <= n;i ++ )
   ans = (ans + quick(m,i)) % mod;
 
   cout << (ans + mod - res) % mod<< endl;
   
}
signed main()
{
    init();
    int T = 1;
    while(T -- ) solve();
    return 0;
}

四、E - Cactus Wall

  • 思路: 观察发现,假设没有仙人掌,那放的方式就是交叉放置,也就是必须得斜着走,走的方式还得满足 |xi - xi-1| = |yi - yi-1| = 1,否则肯定不行,那问题就转化为求从(x , 1)到 (y, m)的最短路径,同时权值也是只有0 和 1,权值为 0 的时候,就是当前位置走到的位置是 仙人掌,否则权值就是1,这里就用双端队列来求解就行,然后记录前驱,注意细节 ,这里面建立动态数组,建立静态会 ME
  • 代码:
#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define fi first
#define se second
#define ios ios::sync_with_stdio(false),cin.tie(0)
#define PII pair<int,int>
using namespace std;
const int N = 4e5 + 100,mod = 1e9 + 7,INF = 1e9;
int n,m;
int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, -1, 1};

bool check(string *s,int x,int y)
{
    if(x < 1 || x > n || y < 1 || y > m) return false;
    if(x + 1 <= n && s[x + 1][y] == '#') return false;
    if(x - 1 >= 1 && s[x - 1][y] == '#') return false;
    if(y + 1 <= m && s[x][y + 1] == '#') return false;
    if(y - 1 >= 1 && s[x][y - 1] == '#') return false;
    
    return true;
}

void solve()
{
    cin >> n >> m;
    string s[n + 1];
    for(int i = 1;i <= n;i ++ ) cin >> s[i],s[i] = "?" + s[i];
    deque<PII> q;
    vector<vector<PII> > pre(n + 2,vector<PII> (m + 2));
    vector<vector<int> > dist(n + 2,vector<int> (m + 2,INF));
  
    for(int i = 1;i <= n;i ++ )
    {
        if(s[i][1] == '#') // 权值为0
        {
            dist[i][1] = 0;
            q.push_front({i,1});
        }
        else if(check(s,i,1))
        {
            dist[i][1] = 1;// 权值为1
            q.push_back({i,1});
        }
    }
    
     while(q.size())
     {
        auto t = q.front();q.pop_front();
        for(int i = 0;i < 4;i ++ )
        {
            int x = dx[i] + t.fi,y = dy[i] + t.se;
            if(check(s,x,y)) // 代表当前这个点能走
            {
                if(dist[t.fi][t.se] + (s[x][y] != '#') < dist[x][y])
                {
                pre[x][y] = {t.fi,t.se};// 记录前驱
                dist[x][y] = dist[t.fi][t.se] + (s[x][y] != '#');
                if(s[x][y] == '#')
                q.push_front({x,y});
                else
                q.push_back({x,y});
                }
                
            }
        }
        
     }
     
      // 寻找前驱
      int x = 0,y = 0;
      int ans = INF;
      for(int i = 1;i <= n;i ++ ) 
      {
          if(ans > dist[i][m])
          {
              ans = dist[i][m];
              x = i,y = m;
          }
      }
      if(ans == INF) cout << "NO" << endl;
      else
      {
          cout << "YES" << endl;
           while(1)
      {
          if(s[x][y] == '.') s[x][y] = '#';
          if(y == 1) break;
          auto p = pre[x][y];
          x = p.fi;
          y = p.se;
      }
      
      for(int i = 1;i <= n;i ++ )
      {
          for(int j = 1;j <= m;j ++ )
          cout << s[i][j];
          cout << endl;
      }
      
      }
     
    
}

signed main()
{
    ios; int T; cin >> T;
    while(T -- ) solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值