The 2021 Sichuan Provincial Collegiate Programming Contest

今年年初就和深爷策划去打星参加一场ICPC,正好今年5月底公司多送了2天年假,于是找来了还在电科读研的师弟林喵喵,组了一支退役旅游队。chenjb的这套题出得蛮好,总体来说题目难度不大,但很多题目却很有意思。本老年人成为全场最坑,感谢喵喵和深爷不嫌我菜,带我玩。最终我们只通过了7题,果然退役很久之后越来越菜。结果不是很满意吧,但是能再次去打onsite,真的很开心,彷佛又回到了20岁QAQ

A.Chuanpai
分析:水题

#include "bits/stdc++.h"
using namespace std;
int T;
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d", &T);
  while (T--) {
    int k;
    scanf("%d", &k);
    int cnt = 0;
    for (int i = 1; i <= k; i++) {
      for (int j = i; j <= k; j++) {
        if (i + j == k) {
          if (i >= 1 && j >= 1 && i <= 6 && j <= 6) cnt++;
        }
      }
    }
    printf("%d\n", cnt);
  }
  return 0;
}

B.Hotpot
分析: 2 n 2n 2n次就会是一个循环,所以只需要模拟 2 n 2n 2n次以内的所有情况即可

#include "bits/stdc++.h"
using namespace std;
const int maxn = 1e5 + 10;
int T, n, k, m, vis[maxn];
pair<int, int> p[maxn];
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d", &T);
  while (T--) {
    memset(vis, 0, sizeof(vis));
    scanf("%d%d%d", &n, &k, &m);
    for (int i = 0; i < n; i++) {
      int x;
      scanf("%d", &x);
      p[i] = make_pair(x, 0);
    }
    if (m >= 2 * n) {
      for (int i = 0; i < 2 * n; i++) {
        int a = i % n;
        if (vis[p[a].first] == 0)
          vis[p[a].first]++;
        else {
          vis[p[a].first]--;
          p[a].second++;
        }
      }
      int tmp = m / (2 * n);
      for (int i = 0; i < n; i++) p[i].second *= tmp;
    }
    int mod = m % (2 * n);
    for (int i = 0; i < mod; i++) {
      int a = i % n;
      if (vis[p[a].first] == 0)
        vis[p[a].first]++;
      else {
        vis[p[a].first]--;
        p[a].second++;
      }
    }
    for (int i = 0; i < n; i++) {
      if (i == n - 1)
        printf("%d\n", p[i].second);
      else
        printf("%d ", p[i].second);
    }
  }
  return 0;
}

D.Rock Paper Scissors
分析:先手的所有出牌情况都不对胜负产生影响,只有后手影响胜负,所以后手优先考虑会胜利的,再考虑平局的,最后考虑会扣分的

#include "bits/stdc++.h"
using namespace std;
int T;
typedef long long LL;
LL br, bp, bs, dr, dp, ds;
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d", &T);
  while (T--) {
    scanf("%lld%lld%lld", &br, &bp, &bs);
    scanf("%lld%lld%lld", &dr, &dp, &ds);
    LL cnt = 0;
    if (br) {
      // cout << dp << endl;
      if (dp && br) {
        LL tmp = min(dp, br);
        cnt += tmp;
        dp -= tmp;
        br -= tmp;
      }
      // cout << "--->" << br << "  " << dp << endl;
      if (dr && br) {
        LL tmp = min(dr, br);
        dr -= tmp;
        br -= tmp;
      }
      if (ds && br) {
        LL tmp = min(ds, br);
        cnt -= tmp;
        br -= tmp;
        ds -= tmp;
      }
    }
    if (bp) {
      if (ds && bp) {
        LL tmp = min(ds, bp);
        cnt += tmp;
        ds -= tmp;
        bp -= tmp;
      }
      if (dp && bp) {
        LL tmp = min(dp, bp);
        dp -= tmp;
        bp -= tmp;
      }
      if (dr && bp) {
        LL tmp = min(dr, bp);
        cnt -= tmp;
        dr -= tmp;
        bp -= tmp;
      }
    }
    if (bs) {
      if (dr && bs) {
        LL tmp = min(dr, bs);
        cnt += tmp;
        dr -= tmp;
        bs -= tmp;
      }
      if (ds && bs) {
        LL tmp = min(ds, bs);
        ds -= tmp;
        bs -= tmp;
      }
      if (bs && dp) {
        LL tmp = min(bs, dp);
        cnt -= tmp;
        bs -= tmp;
        dp -= tmp;
      }
    }
    printf("%lld\n", cnt);
  }
  return 0;
}

E.Don’t Really Like How The Story Ends
分析:
( 1 ) (1) (1)如果 v v v v + 1 v + 1 v+1 直接相连,则访问搜索 v + 1 v + 1 v+1
( 2 ) (2) (2)如果 v v v点存在没有访问的相邻节点且 v v v点不与 v + 1 v + 1 v+1点相连,此时必须将 v + 1 v + 1 v+1 连接到 v v v
( 3 ) (3) (3)如果 v v v点所有相邻节点都被访问了, v + 1 v + 1 v+1 可以与 v v v相连,也可以和从 1 = > v 1 =>v 1=>v中路径上的任意一点相连,路径上的点都在递归栈里面,此时可以让 v v v点退栈,一直回到某个满足条件 1 1 1 2 2 2的节点
( 4 ) (4) (4)如果第 3 3 3点的点退栈一直到起点1号点都没有直接相连,说明存在不连通部分,此时必须加边,此时再去搜索不连通的部分,一直到全部搜索完

#include "bits/stdc++.h"
using namespace std;
const int maxn=1e5+100;
int n,m,T;
vector<int>g[maxn];
int vis[maxn],nxt,cnt;
void dfs(int u){
    nxt++;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(vis[v]) continue;
        if(v==nxt){
            vis[nxt]=1;
            dfs(nxt);
        }else{
            cnt++;
            vis[nxt]=1;
            dfs(nxt);
            i--;
        }
    }
    if(u==1){
        while(nxt<=n){
            vis[nxt]=1;
            dfs(nxt);
            cnt++;
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        for(int i=0;i<=n;i++) {
            g[i].clear();
            vis[i]=0;
        }
        cnt=0,nxt=1;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x<y) g[x].push_back(y);
            if(y<x) g[y].push_back(x);
        }
        for(int i=1;i<=n;i++) sort(g[i].begin(),g[i].end());
        vis[1]=1;
        dfs(1);
        printf("%d\n",cnt);
    }
    return 0;
}

F.Direction Setting
分析:最小费用流
对于一条边 ( l , r ) ( l , r ) (l,r) 要么指向 l l l要么指向 r r r,对于 l l l来说,前 a i a_i ai条指向自己的边对结果贡献为0,只有从第 a i + 1 a_i+1 ai+1条开始,每增加 1 1 1条,权值增加 1 1 1,对于每个点来说,一定是先走免费的,再走收费的,其实就是跑一个最小费用流
具体连边为:
把第 i i i条边抽象为点 m i m_i mi,往 l i l_i li r i r_i ri连边,容量为1,费用为0;源点 s s s m i m_i mi连,容量为1,费用为0;对于每个点 u u u,往汇点连 1 1 1条容量为 a u a_u au,费用为0的, 1 1 1条容量为 I N F INF INF,费用为 1 1 1

#include "bits/stdc++.h"
using namespace std;
const int maxn = 3e5 + 100;
const int INF = 0x3f3f3f3f;
int dis[maxn], vis[maxn], flow[maxn], pre[maxn], id[maxn];
int a[maxn], l[maxn], r[maxn];
int n, m, s, t;
struct edge {
  int to, nxt, flow, w;
} d[maxn];
int head[maxn], cnt = 1;
void add(int u, int v, int flow, int w) {
  d[++cnt] = (edge){v, head[u], flow, w}, head[u] = cnt;
  d[++cnt] = (edge){u, head[v], 0, -w}, head[v] = cnt;
}
bool spfa(int s,int t,int mx){
    for(int i=0;i<=mx;i++) dis[i]=INF,vis[i]=0;
    queue<int>q; q.push(s);
    dis[s]=0,flow[s]=INF;
    while(q.size()){
        int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=d[i].nxt){
            int v=d[i].to;
            if(d[i].flow&&dis[u]+d[i].w<dis[v]){
                dis[v] = dis[u]+d[i].w,pre[v]=i;
                flow[v]=min(d[i].flow,flow[u]);
                if(!vis[v]) q.push(v),vis[v]=1;
            }
        }
    }
    return dis[t]!=INF;
}
int Cost_Flow(int s,int t,int mx){
    int mincost=0;
    while(spfa(s,t,mx)){
        mincost+=dis[t]*flow[t];
        int x=t,i;
        while(x!=s){
            i=pre[x];
            d[i].flow-=flow[t],d[i^1].flow+=flow[t];
            x=d[i^1].to;
        }
    }
    return mincost;
}
int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=m;i++) scanf("%d%d",&l[i],&r[i]);
        s=0,t=n+m+1;
        for(int i=1;i<=m;i++){
            add(s,i,1,0);
            id[i]=cnt+1;
            add(i,m+l[i],1,0);
            add(i,m+r[i],1,0);
        }
        for(int i=1;i<=n;i++){
            add(m+i,t,a[i],0);
            add(m+i,t,INF,1);
        }
        printf("%d\n",Cost_Flow(s,t,n+m+1));
        for(int i=1;i<=m;i++) printf("%d",d[id[i]].flow==0?1:0);
        printf("\n");
        cnt =1;
        for(int i=0;i<=t;i++) head[i]=0;
    }
    return 0;
}

H.Nihongo wa Muzukashii Desu
分析:无脑模拟

#include "bits/stdc++.h"
using namespace std;
int T;
map<string, string> mp;
void init() {
  mp["imasu"] = "tte";
  mp["mimasu"] = "nde";
  mp["kimasu"] = "ite";
  mp["gimasu"] = "ide";
  mp["shimasu"] = "shite";
  mp["chimasu"] = "tte";
  mp["rimasu"] = "tte";
  mp["bimasu"] = "nde";
  mp["nimasu"] = "nde";
}
bool check(string a, string b) {
  int i = 0, j = a.length() - 1;
  // cout << a << " " << endl;
  while (i < j) {
    swap(a[i], a[j]);
    i++, j--;
  }
  // cout << a << " " << endl;
  i = 0;
  // cout << b << " " << endl;
  int k = b.length() - 1;
  while (i < k) {
    swap(b[i], b[k]);
    i++, k--;
  }
  // cout << b << " " << endl;
  int lena = a.length(), lenb = b.length();
  i = 0;
  while (i < lena && i < lenb) {
    if (a[i] == b[i])
      i++;
    else
      return false;
  }
  return true;
}
int main() {
  // freopen("in.txt", "r", stdin);
  init();
  cin >> T;
  while (T--) {
    string s;
    cin >> s;
    string res = "";
    if (check(s, "chimasu")) {
      res += s.substr(0, s.length() - 7) + mp["chimasu"];
    } else if (check(s, "rimasu")) {
      res += s.substr(0, s.length() - 6) + mp["rimasu"];
    } else if (check(s, "bimasu")) {
      res += s.substr(0, s.length() - 6) + mp["bimasu"];
    } else if (check(s, "nimasu")) {
      res += s.substr(0, s.length() - 6) + mp["nimasu"];
    } else if (check(s, "kimasu")) {
      if (s == "ikimasu")
        res = "itte";
      else
        res += s.substr(0, s.length() - 6) + mp["kimasu"];
    } else if (check(s, "gimasu")) {
      res += s.substr(0, s.length() - 6) + mp["gimasu"];
    } else if (check(s, "shimasu")) {
      res += s.substr(0, s.length() - 7) + mp["shimasu"];
    } else if (check(s, "mimasu")) {
      res += s.substr(0, s.length() - 6) + mp["mimasu"];
    } else if (check(s, "imasu")) {
      res += s.substr(0, s.length() - 5) + mp["imasu"];
    } else
      res = s;
    cout << res << endl;
  }
  return 0;
}

J - Ants
分析:解题的关键结论在于两只蚂蚁发生碰撞,可以认为直接穿过。在挡板消失之前,每个蚂蚁在每一轮中会移动 2 L 2L 2L的距离,挡板被撞击 n n n次。因此只用模拟最后一轮的情况即可。两块挡板都维护一个队列,维护撞到这块挡板的蚂蚁的时间序列,每次在两个队头中选择一个最小值,模拟之后塞进另一个队列即可。

#include "bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn = 2e6 + 100;
const LL L = 1e9 + 1;
int n, d[maxn], t1, t2, h1, h2;
LL a, b, p[maxn], q1[maxn], q2[maxn], ans;
void solve1() {
  LL u = q1[++h1];
  ans = max(ans, u);
  if (a) {
    --a;
    q2[++t2] = u + L;
  }
}
void solve2() {
  LL u = q2[++h2];
  ans = max(ans, u);
  if (b) {
    --b;
    q1[++t1] = u + L;
  }
}
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d%lld%lld", &n, &a, &b);
  for (int i = 1; i <= n; i++) scanf("%lld", &p[i]);
  for (int i = 1; i <= n; i++) scanf("%d", &d[i]);
  LL tmp = min(a, b) / (LL)n;
  a -= tmp * n, b -= tmp * n;
  LL s = tmp * 2 * L;
  for (int i = 1; i <= n; i++)
    if (d[i] == 0) q1[++t1] = p[i];
  for (int i = n; i >= 1; i--)
    if (d[i] == 1) q2[++t2] = L - p[i];
  while (h1 < t1 || h2 < t2) {
    if (h2 >= t2 || (h1 < t1 && q1[h1] < q2[h2]))
      solve1();
    else
      solve2();
  }
  printf("%lld\n", s + ans);
  return 0;
}

K. K-skip Permutation
分析:按剩余类分组

#include "bits/stdc++.h"
using namespace std;
const int maxn = 1e6 + 100;
int vis[maxn], n, k;
vector<int> vec[maxn];
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d%d", &n, &k);
  for (int i = 1; i <= n; i++) {
    int mod = i % k;
    vec[mod].push_back(i);
  }
  int cnt = 0;
  for (int i = 0; i < k; i++) {
    for (int j = 0; j < vec[i].size(); j++) {
      cnt++;
      if (cnt == n)
        printf("%d\n", vec[i][j]);
      else
        printf("%d ", vec[i][j]);
    }
  }
  return 0;
}

L. Spicy Restaurant
分析:令 d p ( i , j ) dp(i,j) dp(i,j)表示距离 i i i位置切好等于 j j j的最小值,则最多跑 100 100 100 B F S BFS BFS,可以得到最终答案

#include "bits/stdc++.h"
using namespace std;
const int maxn = 2e5 + 100;
const int INF = 0x3f3f3f3f;
int n, m, q, w[maxn], dp[maxn][100 + 10];
vector<int> g[maxn];
void bfs(int num) {
  queue<int> que;
  for (int i = 1; i <= n; i++)
    if (w[i] == num) {
      que.push(i);
      dp[i][num] = 0;
    }
  while (!que.empty()) {
    int u = que.front();
    que.pop();
    for (auto v : g[u]) {
      if (dp[v][num] != INF) continue;
      que.push(v);
      dp[v][num] = dp[u][num] + 1;
    }
  }
}
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d%d%d", &n, &m, &q);
  for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
  for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= 100; j++) dp[i][j] = INF;
  }
  for (int i = 1; i <= m; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    g[x].push_back(y);
    g[y].push_back(x);
  }
  for (int i = 1; i <= 100; i++) bfs(i);
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= 100; j++) dp[i][j] = min(dp[i][j], dp[i][j - 1]);
  while (q--) {
    int p, a;
    scanf("%d%d", &p, &a);
    printf("%d\n", dp[p][a] == INF ? -1 : dp[p][a]);
  }
  return 0;
}

M.True Story
分析:只需要看最多有多少个满足的即可

#include "bits/stdc++.h"
using namespace std;
const int maxn = 1e5 + 10;
int n, k, x, p0;
int s[maxn], t[maxn], p[maxn];
int main() {
  // freopen("in.txt", "r", stdin);
  scanf("%d%d%d%d", &n, &k, &x, &p0);
  int ans = p0;
  for (int i = 1; i <= n; i++) scanf("%d", &s[i]);
  for (int i = 1; i <= k; i++) scanf("%d", &t[i]);
  for (int i = 1; i <= k; i++) scanf("%d", &p[i]);
  for (int i = 1; i <= k; i++) ans = max(ans, p[i] - t[i]);
  int cnt = 0;
  for (int i = 1; i <= n; i++) {
    int tmp = (x + s[i] - 1) / s[i];
    if (tmp <= ans) cnt++;
  }
  printf("%d\n", cnt);
  return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gzgywh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值