广东外语外贸大学第15届程序设计竞赛题解

谢谢大家参与本次比赛~!

 

 

难度分配

简单题:ABCFH

中等题:DE

难题:GIJ

 

 

A

直接输入四个字符串,直接判断输出即可。无坑点

#include<iostream>#include<string>using namespace std;int main(){    string a,b,c,d;    while(cin>>a>>b>>c>>d){        d.pop_back();        cout<<"I can "<<c<<" "<<d<<"!"<<endl;    }    return 0;}

 

B

用map实现每一个操作即可,具体看代码。如果不会map可以把每一个字符串hash成数字。

有一个坑点,就是在还没教会机器人的时候,问题就已经被禁止回答了,所以要用多个map去标记。

#include <iostream>#include <string>#include <map>using namespace std;int main(){  map<string, string> mp;  map<string, int> vis;  string a, b;  int Q;  scanf("%d", &Q);  while (Q--)  {    int t;    scanf("%d", &t);    if (t == 1)    {      cin >> a >> b;      mp[a] = b;      if (vis[a] == 0)        vis[a] = 1;    }    if (t == 2)    {      cin >> a;      if (mp[a] != "" && vis[a] == 1)      {        cout << mp[a] << endl;      }      else      {        cout << "I don't know what to say!" << endl;      }    }    if (t == 3)    {      cin >> a;      vis[a] = 2;    }    if (t == 4)    {      cin >> a;      vis[a] = 1;    }  }  return 0;}

 

C

多写几项找规律得到公式

#include <iostream>using namespace std;typedef long long ll;int main(){  ll N;  while (cin >> N)  {    if (N % 2 == 0)      cout << N * N + N / 2 << endl;    else      cout << N * N << endl;  }  return 0;}

 

D

数据范围小,直接上Floyd求传递闭包。很经典的一道题目。离散数学有教。注意循环的顺序。

#include <iostream>using namespace std;typedef long long ll;bool G[305][305];int main(){  int N;  scanf("%d", &N);  for (int i = 1; i <= N; i++)    for (int j = 1; j <= N; j++)      scanf("%d", &G[i][j]);  for (int k = 1; k <= N; ++k)  {    for (int i = 1; i <= N; ++i)    {      for (int j = 1; j <= N; ++j)      {        if (G[i][j] == 1 || (G[i][k] == 1 && G[k][j] == 1))        {          G[i][j] = 1;        }      }    }  }  int Q;  scanf("%d", &Q);  while (Q--)  {    int u, v;    scanf("%d%d", &u, &v);    printf("%d\n", G[u][v]);  }  return 0;}

 

E

很经典的线段树维护最大值,没有任何坑点。这里没有必要开四个线段树,具体看代码实现!

 

#include <iostream>#include <string.h>#include <map>using namespace std;#define mod 10000007typedef long long ll;const int MAXN = 100005;ll tree[MAXN << 2];ll LI[4][MAXN];void pushup(int rt){  tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);}void update(int K, int C, int X, int l, int r, int rt){  if (l == r)  {    LI[K][C] = X;    tree[rt] = LI[0][C] * LI[1][C] * LI[2][C] * LI[3][C];    return;  }  int m = (l + r) / 2;  if (C <= m)    update(K, C, X, l, m, rt << 1);  else    update(K, C, X, m + 1, r, rt << 1 | 1);  pushup(rt);}ll query(int L, int R, int l, int r, int rt){  if (L <= l && r <= R)  {    return tree[rt];  }  ll ans = 0x3f3f3f3f3f3f3f3f;  ans = -ans;  int m = (l + r) / 2;  if (L <= m)    ans = max(ans, query(L, R, l, m, rt << 1));  if (R > m)    ans = max(ans, query(L, R, m + 1, r, rt << 1 | 1));  return ans;}int main(){  int N, Q;  cin >> N >> Q;  while (Q--)  {    int t, K, C, X, L, R;    cin >> t;    if (t == 1)    {      cin >> K >> C >> X;      update(K - 1, C, X, 1, N, 1);    }    else    {      cin >> L >> R;      cout << query(L, R, 1, N, 1) << endl;    }  }  return 0;}

 

F

暴力枚举即可,提取每一位,看看有没有重复数字,没有重复就输出。

 

#include <iostream>#include <cstring>using namespace std;bool vis[10];int num[3];void solve(int n){  if (n == 3 && num[0] * 2 == num[1] && num[0] * 3 == num[2])    cout << num[0] << ' ' << num[1] << ' ' << num[2] << endl;  for (int i = 1; i <= 9; i++)  {    if (!vis[i])    {      vis[i] = true;      for (int j = 1; j <= 9; j++)      {        if (!vis[j])        {          vis[j] = true;          for (int k = 1; k <= 9; k++)          {            if (!vis[k])            {              vis[k] = true;              num[n] = i * 100 + j * 10 + k;              solve(n + 1);              vis[k] = false;            }          }          vis[j] = false;        }      }      vis[i] = false;    }  }}int main(){  string a;  cin >> a;  memset(vis, false, sizeof(vis));  solve(0);}

 

G

很经典的一道题目。考察二分+最小生成树

我们考虑将所有的白色的边加上一个cost,如x至y有一条白色边权值为len,那我们将它的权值变为cost+len,然后我们做最小生成树,很明显,如果加上的cost越大,所选的白色边越少,所以我们就二分这个cost,直至刚好选了need条时,则答案就是生成树的权值-need*cost。

 

#include <iostream>#include <set>#include <map>#include <cstdio>#include <cstring>#include <cstdlib>#include <ctime>#include <vector>#include <queue>#include <algorithm>#include <cmath>#define inf 1000000000#define pa pair<int, int>#define ll long longusing namespace std;int read(){  int x = 0, f = 1;  char ch = getchar();  while (ch < '0' || ch > '9')  {    if (ch == '-')      f = -1;    ch = getchar();  }  while (ch >= '0' && ch <= '9')  {    x = x * 10 + ch - '0';    ch = getchar();  }  return x * f;}int n, m, cnt, tot, ned;int sumv;int u[100005], v[100005], w[100005], c[100005];int fa[100005];struct edge{  int u, v, w, c;} e[100005];bool operator<(edge a, edge b){  return a.w == b.w ? a.c < b.c : a.w < b.w;}int find(int x){  return x == fa[x] ? x : fa[x] = find(fa[x]);}bool check(int x){  tot = cnt = 0;  for (int i = 1; i <= n; i++)    fa[i] = i;  for (int i = 1; i <= m; i++)  {    e[i].u = u[i], e[i].v = v[i], e[i].w = w[i];    e[i].c = c[i];    if (!c[i])      e[i].w += x;  }  sort(e + 1, e + m + 1);  for (int i = 1; i <= m; i++)  {    int p = find(e[i].u), q = find(e[i].v);    if (p != q)    {      fa[p] = q;      tot += e[i].w;      if (!e[i].c)        cnt++;    }  }  return cnt >= ned;}int main(){  n = read();  m = read();  ned = read();  for (int i = 1; i <= m; i++)  {    u[i] = read(), v[i] = read(), w[i] = read(), c[i] = read();    u[i]++;    v[i]++;  }  int l = -105, r = 105;  while (l <= r)  {    int mid = (l + r) >> 1;    if (check(mid))      l = mid + 1, sumv = tot - ned * mid;    else      r = mid - 1;  }  printf("%d", sumv);  return 0;}

 

 

H

模拟题。暴力枚举端点即可,但是不能用 N^3 的复杂度。第二个端点其实不用枚举,直接取中点即可,优化到了 N^2.

注意A 不能和B相等。像aaa这种输入就得输出SAD而不是a a a。

 

 

#include <iostream>#include <string.h>#include <map>#include <vector>using namespace std;#define mod 10000007typedef long long ll;char str[505];int main(){  int T;  scanf("%d", &T);  while (T--)  {    scanf("%s", str);    int len = strlen(str);    bool yes = 1;    for (int i = 1; i <= len - 2; i++)    {      if ((len - i) % 2 == 1)        continue;      string a, b, c;      for (int k = 0; k < i; k++)        a.push_back(str[k]);      for (int k = i; k < i + (len - i) / 2; k++)        b.push_back(str[k]);      for (int k = i + (len - i) / 2; k < len; k++)        c.push_back(str[k]);      if (a != b && b == c)      {        cout << a << " " << b << " " << c << endl;        yes = 0;        break;      }    }    if (yes)      cout << "SAD" << endl;  }  return 0;}

 

 

I

数学题

要推出任意次方和公式

 

 

 

我们会发现,求k次和的时候,需要用到k-1,k-2…,0的结果。

所以实际求的时候,可以反过来逐个计算。

组合数我们可以事先计算好,除以k+1,也可以预处理乘法逆元。

所以总的复杂度O(k^2)。

 

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 1e9 + 7;const int N = 2050;int T, y;LL x, C[N][N], rev[N], dp[N];LL pow_mod(LL x, LL p){  LL s = 1;  while (p)  {    if (p & 1)      s = s * x % mod;    x = x * x % mod;    p >>= 1;  }  return s;}void init(){  rev[0] = 1;  C[0][0] = 1;  for (int i = 1; i < N; i++)  {    C[i][0] = C[i][i] = 1;    for (int j = 1; j < i; j++)    {      C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;    }    rev[i] = pow_mod(i, mod - 2);  }}LL solve1(LL x, int y){  x = x % mod;  dp[0] = x;  LL n = (x + 1) % mod;  for (int i = 1; i <= y; i++)  {    dp[i] = (pow_mod(n, i + 1) + mod - 1) % mod;    for (int j = 1; j <= i; j++)    {      dp[i] = (dp[i] - C[i + 1][j + 1] * dp[i - j] % mod) % mod;    }    if (dp[i] < 0)      dp[i] += mod;    dp[i] = dp[i] * rev[i + 1] % mod;  }  return dp[y];}LL solve2(int x, LL y){  LL ret = 0;  for (int i = 1; i <= x; i++)  {    ret = (ret + pow_mod(i, y)) % mod;  }  return ret;}int main(){  init();  scanf("%d", &T);  while (T--)  {    scanf("%lld %d", &x, &y);    printf("%lld\n", (solve1(x, y) + solve2(y, x)) % mod);  }  return 0;}

 

 J

一道防AK题

 

做法是 主席树+字典树+二分

我们先把所有ADD的字符串插入到一颗字典树。实际上每一次查询,就是查询字典树某个子树中某一层的节点的权值和。

首先我们可以用dfs序给字典树每一个节点编号记为dfn,然后再做一次宽搜,给每一个节点再编一次编号记为bfn。

然后我们记录字典树每一层的节点都有哪些。

然后我们再用把树拍扁的思想,记录每一个节点所代表的区间。然后建一棵线段树。

然后对于ADD操作,我们直接找到那个字符串在字典树中的最后一个节点,对那个bfn所在地方+1,用线段树维护和。

然后对于QUERY操作,我们直接找到那个前缀所在的节点的那个子树,然后要找到,所要查询那个长度所在的层的哪些编号属于那个子树。

对于这个需求,我们直接对那一层的节点二分,二分值是节点所代表的的区间。这样子我们就能确定那一层哪些节点属于那个子树了。

对于回退问题,我们直接用可持久化线段树解决

 

额外的的解释:

全部读入,构建字典树,记录下每个ADD和QUERY的字符串末尾对应的节点ID;

对字典树做dfs

记录dfs序,dfs_lft[x]和dfs_rgt[x]分别表示以x为根的子树中的最小dfs序和最大dfs序;

将深度相同的节点放到一个数组,按dfs序升序排序,同时记录对应的节点ID;

按照节点深度和dfs序重新排列,即第一层所有节点的dfs序,第二层所有节点的dfs序…

同时记录每个节点跟这个新序列的映射关系idx[x];

对这个序列构建主席树;

对于ADD操作,主席树开新版本,找到对应的idx进行加1;

对于REVERT操作,直接将当前版本指向指定版本;

对于QUERY操作,先找到对应的子树根节点x,根据dfs_lft[x]和dfs_rgt[x],二分出在相应深度的dfs序,因为在x的子节点的dfs序必定大于等于dfs_lft[x],小于等于dfs_rgt[x],而非其子节点必定不满足此条件。根据这个dfs序范围反查出两端节点ID,进而转化为主席树的区间端点,然后进行简单的区间求和操作即可。

 

标程1:

#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int T, Q;char op[10], ss[N];int ch[N][26], oper[N], num[N], dep[N], node_id[N];int sz, max_dep, dfs_cnt, dfs_lft[N], dfs_rgt[N], idx[N];vector<int> V[N], G[N];int n, node_cnt, tree[N], S[N * 20], lson[N * 20], rson[N * 20];struct Trie{  void init(){    sz = 1;    memset(ch[0], 0, sizeof(ch[0]));  }  int add(){    int len = strlen(ss);    int u = 0, c;    for (int i = 0; i < len; i++)    {      c = ss[i] - 'a';      if (!ch[u][c])      {        memset(ch[sz], 0, sizeof(ch[sz]));        ch[u][c] = sz++;      }      u = ch[u][c];    }    return u;  }  void dfs(int u){    max_dep = max(max_dep, dep[u]);    dfs_lft[u] = ++dfs_cnt;    V[dep[u]].push_back(dfs_cnt);    G[dep[u]].push_back(u);    for (int i = 0; i < 26; i++)    {      if (ch[u][i])      {        dep[ch[u][i]] = dep[u] + 1;        dfs(ch[u][i]);      }    }    dfs_rgt[u] = dfs_cnt;  }} tr;int build(int L, int R){  int ret = ++node_cnt;  S[ret] = 0;  if (L < R)  {    int mid = L + R >> 1;    lson[ret] = build(L, mid);    rson[ret] = build(mid + 1, R);  }  else  {    lson[ret] = rson[ret] = 0;  }  return ret;}int add_node(int root0, int p, int v){  int L = 1, R = n, mid;  int root = ++node_cnt;  int ret = root;  S[root] = S[root0] + v;  while (L < R)  {    mid = L + R >> 1;    if (p <= mid)    {      lson[root] = ++node_cnt;      rson[root] = rson[root0];      root = lson[root];      root0 = lson[root0];      R = mid;    }    else    {      lson[root] = lson[root0];      rson[root] = ++node_cnt;      root = rson[root];      root0 = rson[root0];      L = mid + 1;    }    S[root] = S[root0] + v;  }  return ret;}int sum(int root, int L, int R, int ll, int rr){  if (L == ll && R == rr)  {    return S[root];  }  int mid = L + R >> 1;  if (rr <= mid)    return sum(lson[root], L, mid, ll, rr);  if (ll > mid)    return sum(rson[root], mid + 1, R, ll, rr);  return sum(lson[root], L, mid, ll, mid) + sum(rson[root], mid + 1, R, mid + 1, rr);}int query(int root, int node, int len){  if (len > max_dep || dep[node] > len)    return 0;  if (V[len].size() == 0)    return 0;  int lft = dfs_lft[node];  int rgt = dfs_rgt[node];  if (rgt < V[len][0] || lft > V[len].back())    return 0;  int low, top, mid, key_l, key_r;  low = 0;  top = V[len].size() - 1;  key_l = top;  while (low <= top)  {    mid = low + top >> 1;    if (V[len][mid] >= lft)    {      key_l = min(key_l, mid);      top = mid - 1;    }    else    {      low = mid + 1;    }  }  low = 0;  top = V[len].size() - 1;  key_r = 0;  while (low <= top)  {    mid = low + top >> 1;    if (V[len][mid] <= rgt)    {      key_r = max(key_r, mid);      low = mid + 1;    }    else    {      top = mid - 1;    }  }  if (key_l > key_r)    return 0;  int idx_lft = idx[G[len][key_l]];  int idx_rgt = idx[G[len][key_r]];  return sum(root, 1, n, idx_lft, idx_rgt);}void init(){  dfs_cnt = 0;  max_dep = 0;  dep[0] = 0;  tr.dfs(0);  n = 0;  for (int i = 1; i <= max_dep; i++)  {    for (int j = 0; j < G[i].size(); j++)    {      idx[G[i][j]] = ++n;    }  }  node_cnt = 0;  tree[0] = build(1, n);}int main(){  scanf("%d", &Q);  tr.init();  for (int i = 1; i <= Q; i++)  {    scanf("%s", op);    if (op[0] == 'A')    {      scanf("%s", ss);      node_id[i] = tr.add();      oper[i] = 0;    }    else if (op[0] == 'Q')    {      scanf("%s %d", ss, num + i);      node_id[i] = tr.add();      oper[i] = 1;    }    else    {      scanf("%d", num + i);      oper[i] = 2;    }  }  init();  for (int i = 1; i <= Q; i++)  {    if (oper[i] == 0)    {      int j = idx[node_id[i]];      tree[i] = add_node(tree[i - 1], j, 1);    }    else if (oper[i] == 1)    {      tree[i] = tree[i - 1];      printf("%d\n", query(tree[i], node_id[i], num[i]));    }    else    {      tree[i] = tree[num[i] - 1];    }  }  for (int i = 0; i <= max_dep; i++)  {    V[i].clear();    G[i].clear();  }  return 0;}

 

标程2:

#include <iostream>#include <string.h>#include <map>#include <vector>#include <queue>#include <map>#include <algorithm>#include <stdio.h>using namespace std;#define MAXN 101005typedef long long ll;int nxt[MAXN][28];int cnt = 0;void insert(string str){    int len = str.size();    int cur = 0;    for (int i = 0; i < len; i++)    {        if (nxt[cur][str[i] - 'a'] == 0)            nxt[cur][str[i] - 'a'] = ++cnt;        cur = nxt[cur][str[i] - 'a'];    }}int getid(string str){    int len = str.size();    int cur = 0;    for (int i = 0; i < len; i++)    {        if (nxt[cur][str[i] - 'a'] == 0)            return 0;        cur = nxt[cur][str[i] - 'a'];    }    return cur;}int ldfn[MAXN];int rdfn[MAXN];int mytime = 0;void dfs(int cur){    ldfn[cur] = ++mytime;    for (int i = 0; i < 26; i++)    {        if (nxt[cur][i])        {            dfs(nxt[cur][i]);        }    }    rdfn[cur] = mytime;}int bfn[MAXN];vector<int> ceng[MAXN * 2];int bt = 1;void bfs(){    queue<pair<int, int>> que;    que.push({0, 0});    while (!que.empty())    {        pair<int, int> tp = que.front();        que.pop();        ceng[tp.first].push_back(tp.second);        for (int i = 0; i < 27; i++)        {            if (nxt[tp.second][i])            {                que.push({tp.first + 1, nxt[tp.second][i]});            }        }    }}int root[MAXN];int mycount = 0;int tree[MAXN * 15];int rs[MAXN * 15];int ls[MAXN * 15];void update(int C, int X, int l, int r, int &rt, int lrt){    rt = ++mycount;    ls[rt] = ls[lrt];    rs[rt] = rs[lrt];    tree[rt] = tree[lrt] + X;    if (l == r)    {        return;    }    int m = (l + r) / 2;    if (C <= m)        update(C, X, l, m, ls[rt], ls[lrt]);    else        update(C, X, m + 1, r, rs[rt], rs[lrt]);}int query(int L, int R, int l, int r, int rt){    if (L <= l && r <= R)    {        return tree[rt];    }    int m = (l + r) / 2;    int ans = 0;    if (L <= m)        ans += query(L, R, l, m, ls[rt]);    if (R > m)        ans += query(L, R, m + 1, r, rs[rt]);    return ans;}struct queryq{    int type;    string str;    int len;} q[100505];char str[50];char tmp[100005];int main(){    int Q;    scanf("%d", &Q);    for (int i = 1; i <= Q; i++)    {        scanf("%s", str);        if (str[0] == 'A')        {            scanf("%s", tmp);            q[i].type = 0;            q[i].str = tmp;            q[i].len = 0;            insert(tmp);        }        else if (str[0] == 'Q')        {            scanf("%s", tmp);            int len;            scanf("%d", &len);            q[i].type = 1;            q[i].str = tmp;            q[i].len = len;        }        else        {            int len;            scanf("%d", &len);            q[i].type = 2;            q[i].str = "";            q[i].len = len;        }    }    dfs(0);    bfs();    for (int i = 0; i <= 100005; i++)    {        for (int j = 0; j < ceng[i].size(); j++)        {            bfn[ldfn[ceng[i][j]]] = bt++;            ceng[i][j] = ldfn[ceng[i][j]];        }    }    cnt++;    for (int i = 1; i <= Q; i++)    {        if (q[i].type == 0)        {            int tp = getid(q[i].str);            update(bfn[ldfn[tp]], 1, 1, cnt, root[i], root[i - 1]);        }        else if (q[i].type == 1)        {            root[i] = root[i - 1];            int tp = getid(q[i].str);            if (tp == 0)            {                printf("0\n");                continue;            }            if (q[i].len < q[i].str.size())            {                printf("0\n");                continue;            }            if (q[i].len > 100005)            {                printf("0\n");                continue;            }            if (ceng[q[i].len].size() == 0)            {                printf("0\n");                continue;            }            if (ldfn[tp] > ceng[q[i].len].back())            {                printf("0\n");                continue;            }            if (rdfn[tp] < ceng[q[i].len].front())            {                printf("0\n");                continue;            }            int l;            int r;            if (ldfn[tp] <= ceng[q[i].len].front())                l = ceng[q[i].len].front();            else                l = *lower_bound(ceng[q[i].len].begin(), ceng[q[i].len].end(), ldfn[tp]);            if (rdfn[tp] >= ceng[q[i].len].back())                r = ceng[q[i].len].back();            else                r = *(upper_bound(ceng[q[i].len].begin(), ceng[q[i].len].end(), rdfn[tp]) - 1);            printf("%d\n", query(bfn[l], bfn[r], 1, cnt, root[i]));        }        else        {            root[i] = root[q[i].len - 1];        }    }    return 0;}

 

 

 

 

 

 

 

 

 

发布了402 篇原创文章 · 获赞 67 · 访问量 16万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 点我我会动 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览