CTFPC-3rd 题解

前言#

比赛直达车。

Github 仓库直达车。

最高分,@haoertiansuo,一如既往地榜一。由于题目难度大幅提高,没有 AK。分数 365/600 分。

第二名,@yhylivedream,E 题用了大肠腧玄学做法,分数 345/600 分。

第三名,@JOE_ZengYuQiao_0928,275/600 分。

B 题没有超过 10 分之人,D 题没有超过 30 分之人,F 题没有超过 25 分之人。

原因:B 题,正解没人想到。D 题,(可能)数据错误(有好事者可以帮忙看一下 Github 仓库,校验 std 和 gen)。F 题,大模拟,没有人愿意做。

榜单直达车。

本文很长,推荐右下角展开目录,跳着看。

A. 摆月饼#

神金搜索,秒了。

注意 Sub 里 n=20 的特殊情况,要特判。

#include <bits/stdc++.h>
#define rty printf("Yes\n");
#define RTY printf("YES\n");
#define rtn printf("No\n");
#define RTN printf("NO\n");
#define rep(v,b,e) for(int v=b;v<=e;v++)
#define repq(v,b,e) for(int v=b;v<e;v++)
#define rrep(v,e,b) for(int v=b;v>=e;v--)
#define rrepq(v,e,b) for(int v=b;v>e;v--)
#define stg string
#define vct vector
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

void solve() {
  
}

int n, p[100][100], pre[100];
bool vis[100];

int ans = 0;
void dfs(int dep) {
  if (dep == n + 1) {
    int res = 0;
    rep(i, 1, n) {
      res += p[i][pre[i]];
    }
	if (res > ans) {
		ans = res;
	}
    return;
  }
  rep(i, 1, n) {
    if (!vis[i]) {
      vis[i] = 1;
      pre[dep] = i;
      dfs(dep + 1);
      vis[i] = 0;
    }
  }
}

main() {
  //  int t; cin >> t; while (t--) solve();
  cin >> n;
  rep(i, 1, n) {
    rep(j, 1, n) {
      cin >> p[i][j];
    }
  }
  if (!p[1][1] && n == 20) {
    int res = 0;
    rep(i, 1, n) {
      rep(j, 1, n) {
        res = max(res, p[i][j]);
      }
    }
    cout << res << endl;
    return 0;
  } else if (n == 20) {
    int res = 0;
    rep(i, 1, n) 
      res += p[i][1];
    cout << res << endl;
    return 0;
  }
  dfs(1);
  cout << ans << endl;
  return 0;
}

B. 不吉利的素数#

抄袭的是这道月赛题。

Paper,需要自取。

使用注意力集中的左轮眼可以想到:我们先需要做预处理,找到一个足够大的数 108,设一个集合 M(S)。对于每一个 x[2,108] 且为素数的 x,如果没有任意 yM(S) 互为对方的子序列,将 x 加入 M(S),最终可以得到一个序列。如果你的注意力比较涣散,我就直接在这里给出 M(S) 罢:

手打觉得麻烦的,给出计算她的 Python 代码:

from math import *
def isSub(s: str, t: str) -> bool:
	index = 0
	for value in s:
		index = t.find(value, index)
		if index == -1:
			return False
		index += 1
	return True

def isprime(n : int) -> bool:
	if n == 2:
		return True
	for i in range(2, n):
		if i * i > n:
			break
		if n % i == 0:
			return False
	return True

MS = []
for i in range(2, int(1e8)):
	stg = str(i)
	flg = 0
	if not isprime(i):
		continue
	if (len(MS)):
		for v in MS:
			if isSub(v, stg) or isSub(stg, v):
				flg = 1
				break	
		if not flg:
			MS.append(stg)
	else:
		MS.append(stg)

print("{", ",".join(MS), "}")

这样输出出来可以直接拿着用。当然,抽象的 Python 会花费您较多的时间(实测 > 60 min,量子计算机和您的 42 号混凝土可能耗时不同),而 C艹也没有较为便捷的实现,有 matlab 等工具的同学自己玩去

接下来才是主要部分:先扫描字符串,匹配,符合打哨兵符。打一次符号计数器加一,代码:

#include <bits/stdc++.h>
using namespace std;

string stg[10][10] = {
    {"2", "3", "5", "7"},
    {"11", "19", "41", "61", "89"},
    {"409", "449", "499", "881", "991"},
    {"6469", "6949", "9001", "9049", "9649", "9949"},
    {"60649"},
    {"666649"},
    {"946669"},
    {"60000049", "66000049", "66600049"}
};

int len_stg[10] = {
    4, 5, 5, 6, 1, 1, 1, 3
};

int main() {
    string s;
    int ans = 0;
    cin >> s;
    for (int j = 0; j < 7; j++) {
        for (int i = 0; i < s.size(); i++) {
            string init = stg[j][0];
            int pt = 0;
            while (pt < len_stg[j]) {
                for (int k = i; k <= i + j; k++) {
                    if (s[k] != init[k - i]) {
                        goto _;
                    }
                }
                s[i] = '@';
                ans++;
                goto __;
                _:;
                init = stg[j][++pt];
            }
            __:;
        }
    }
    cout << ans;
    
}

C. 外婆桥#

Sub1、3(40pts#

暴力,分到手。

Sub2、4(20pts#

数据小,暴力跑得过,所以脑子不动、快读都懒得优化这破题能拿 60 分。

如果你实在懒到抽象,20 分也是轻轻松松的好吧。

Sub 5(40pts#

注意到:只有区间查询,没有区间修改,考虑前缀和。

但是或运算无法使用前缀和,它没有其逆运算,与 maxand 如出一辙。

那么可以想到,它是允许重复计算的,比如要求计算 [2,7]f(2,7)=f(2,4)orf(3,7)。这是因为一个数被或两次还是能得到这个数。

使用注意力集中的左轮眼能想到,它的性质和 max 是一样的,如果你 CSP-J 401 分的话,更容易注意到:和 max 性质一样的算法可以使用包括但不限于:RMQ、ST 表、单调队列、树状数组等算法。

如果你够有实力,能默写 OI-WIKI,使用线段树、分块、ODT、莫队,那我也无以回应。我们这里只考虑更为简易的普通算法,这里附上 ST 表的代码:

#include <bits/stdc++.h>
#define rty printf("Yes\n");
#define RTY printf("YES\n");
#define rtn printf("No\n");
#define RTN printf("NO\n");
#define rep(v,b,e) for(int v=b;v<=e;v++)
#define repq(v,b,e) for(int v=b;v<e;v++)
#define rrep(v,e,b) for(int v=b;v>=e;v--)
#define rrepq(v,e,b) for(int v=b;v>e;v--)
#define stg string
#define vct vector
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

void solve() {
	
}



#define int ull
inline 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;
}
void write(int x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}
const int N = 1E6 + 5;
int ST[N][32];

int query(int l, int r) {
  int k = log2(r - l + 1);
  return ST[l][k] | ST[r - (1 << k) + 1][k];
}

main() {
//	int t; cin >> t; while (t--) solve();
  int n, q;
  n = read(); q = read();
  rep(i, 1, n) {
    ST[i][0] = read();
  }
  for (int j = 1; (1 << j) <= n; j++) {
    for (int i = 1; i <= n - (1 << (j - 1)) + 1; i++) {
      ST[i][j] = ST[i][j - 1] | ST[i + (1 << (j - 1))][j - 1];
    }
  }
  while (q--) {
    int l, r;
    l = read(); r = read();
    write(query(l, r));
    putchar('\n');
  }
	return 0;
}

D. 坐地铁#

Sub1(20pts#

抽象玩意儿,最多一次换乘,数据超级水,为什么我这样说去看 Github 上的 Generator 和数据。

Sub2(10pts#

我们保证一定有解(后来补的)

Sub3(40pts#

其实没屁用这个特殊性质。

Sub4(30pts#

先消去所有线路,对于所有站标记其经过的线路。接着在起点站跑 dfs,数据水,保过:

#include <bits/stdc++.h>
#define rty printf("Yes\n");
#define RTY printf("YES\n");
#define rtn printf("No\n");
#define RTN printf("NO\n");
#define rep(v,b,e) for(int v=b;v<=e;v++)
#define repq(v,b,e) for(int v=b;v<e;v++)
#define rrep(v,e,b) for(int v=b;v>=e;v--)
#define rrepq(v,e,b) for(int v=b;v>e;v--)
#define stg string
#define vct vector
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

void solve() {
  
}
#define int ll
int res = LLONG_MAX;
vct<int> E[806];
bool vis[30][806];
bool visit[806];
int n, s, t;

void dfs(int u, int line, int times) {
  if (u == t) {
    res = min(res, times);
    return;
  }
  if (times > res) {
    return;
  }
  for (int v : E[u]) {
    if (visit[v]) continue;
    if (!vis[line][v]) {
      rep(i, 1, n) {
        if (vis[i][v]) {
          visit[v] = 1;
          dfs(v, i, times + 2);
          visit[v] = 0;
        }
      }
    } else {
      visit[v] = 1;
      dfs(v, line, times + 1);
      visit[v] = 0;
    }
  } 
}

main() {
  //  int t; cin >> t; while (t--) solve();
  cin >> n >> s >> t;
  rep(i, 1, n) {
    int ai;
    cin >> ai;
    int x, last;
    cin >> x;
    vis[i][x] = 1;
    rep(f, 2, ai) {
      last = x;
      cin >> x;
      E[last].push_back(x);
      E[x].push_back(last);
      vis[i][x]= 1;
    }
  } 
  visit[s] = 1;
  rep(i, 1, n) {
    if (vis[i][s]) {
      dfs(s, i, 0);
      memset(visit, 0, sizeof visit);
    }
  }
  cout << res * 5;
  return 0;
}

E. DFbd 的因子积#

感谢圣人 DFbd 的供题!

暴力(40pts#

#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main() {
    int p;
    cin >> p;
    int ans = 1;
    for (int i = 1; i < p; i++) {
        int tmp = 1;
        for (int d = 1; d * d <= i; d++) {
            if (d * d == i) {
                tmp *= d;
                tmp %= p;
            } else if (i % d == 0) {
                tmp *= i;
                tmp %= p;
            }
        }
        ans *= tmp;
        ans %= p;
    }
    cout << ans;
}

正题(100pts#

注意到有:

(i=1p1d|id)modp=(i=1p1i(p1)/i)modp

幂这块用快速幂优化:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int p,ans=1;
int fpow(int a,int b,int p){
	int res=1;
	while(b){
		if(b%2) res=res*a%p;
		a=a*a%p;
		b/=2;
	}
	return res;
}
signed main(){
	cin >> p;
	for(int i=1;i<p;i++) ans=ans*fpow(i,(p-1)/i,p)%p;
	cout << ans;
	return 0;
}

F. 扑克#

这他妈是道大模拟,所以没人帮我验题,是我自己手工计算的。

倍率 2.5,没骗到的同学可惜咯。

Sub1(10pts#

n=1,自娱自乐。注意到这个人是地主,他必定会赢。输出 1,十分到手。

Sub2(40pts#

只考虑:

  • 单牌
  • 对子
  • 王炸

其它都不用考虑。

Sub3(60pts#

这里直接贴代码,稍微讲一下坑点:

  • 判断所有人都要不起,轮原来的人开牌的情况。
  • 开牌的人与出牌的人逻辑要分开,有很大的不同。
  • 三带一、三带二、飞机、大顺都能上炸弹。
  • 上一位玩家出的炸弹,下一位玩家得出更大的炸弹。
  • 王炸无敌。

代码很长(调了 4 天),400 多行,慢慢食用吧:

#include <bits/stdc++.h>
#define rty printf("Yes\n");
#define RTY printf("YES\n");
#define rtn printf("No\n");
#define RTN printf("NO\n");
#define rep(v,b,e) for(int v=b;v<=e;v++)
#define repq(v,b,e) for(int v=b;v<e;v++)
#define rrep(v,e,b) for(int v=b;v>=e;v--)
#define rrepq(v,e,b) for(int v=b;v>e;v--)
#define stg string
#define vct vector
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

void solve() {
  
}

#define PLANE 1
#define BOMB 2
#define COUPLE 3
#define TRIPLE_O 4
#define TRIPLE_T 5
#define SINGLE 6
#define DOUBLE_KING 7
#define STRAIGHT 8

struct Straight { int start, length, end; };
struct Plane { int start, times, length, end; };
struct Bomb { int card; };
struct Couple {int card; };
struct Triple {int carda, cardb; };
struct Single { int card; };

struct LastCard {
  int type;
  Straight straight;
  Plane plane;
  Bomb bomb;
  Couple couple;
  Triple triple;
  Single single;
};

int n;
stg x, y, z;
int c[52][52];
map<stg, int> _m;
stg _mtmp[52] =  {"3","4","5","6","7","8","9","10","J","Q","K","A","2","LJ","BJ"};

bool pk(int x) {
  rep(j, 1, 15) {
    if (c[x][j]) {
      return false;
    }
  }
  return true;
}


main() {
  //  int t; cin >> t; while (t--) solve();
  cin >> n;
  rep(i, 1, 15) {
    _m[_mtmp[i - 1]] = i;
  }
  rep(i, 1, n) {
    rep(j, 1, 51 / n) {
      stg tmp;
      cin >> tmp;
      c[i][_m[tmp]]++;    
    }
  }
  cin >> x >> y >> z;
  int ix = _m[x], iy = _m[y], iz = _m[z];
  int p;
  cin >> p;
  int pt = p;
  bool tmp = 0;
  while (1) {
    if (pt == p && tmp) {
      c[p][ix]++; c[p][iy]++; c[p][iz]++;
      break;
    }
    int sum = 0;
    rep(j, 1, 15) {
      sum += j * c[pt][j]; 
    }
    if (sum % 3 == 1) {
      p = pt;
      c[pt][ix]++; c[pt][iy]++; c[pt][iz]++;
      break;
    }
    pt++;
    if (pt > n) {
      pt = 1;
      tmp = 1;
    }
  }
  pt = p;
  bool flg = 1;
  int lastcp_pt = pt;
  LastCard lastcard;
  while (1) {
    if (pk(lastcp_pt)) {
      pt = lastcp_pt;
      if (pt == p) {
        cout << p;
        return 0;
      } else {
        rep(i, 1, n) {
          if (i == p) {
            continue;
          }
          cout << i << ' ';
        }
        return 0;
      }
    }
    if (flg) {
      rep(start, 1, 12) {
        rrep(end, start + 1, 12) {
          rrep(count_times, 2, 4) {
            if ((end - start + 1) * count_times < 6) {
              continue;
            }
            rep(i, start, end) {
              if (c[pt][i] < count_times) {
                goto next_count_times;
              }
            }
            lastcard.type = PLANE;
            lastcard.plane.start = start;
            lastcard.plane.times = count_times;
            lastcard.plane.length = end - start + 1;
            lastcard.plane.end = end;
            rep(i, start, end) {
              c[pt][i] -= count_times;
            }
            goto lastcp;
            next_count_times:;
          }
        }
      }
      rep(start, 1, 12) {
        rrep(end, start + 1, 12) {
          if (end - start + 1 < 6) {
            continue;
          }
          rep(i, start, end) {
            if (!c[pt][i]) {
              goto next_beginning; 
            }
          }
          rep(i, start, end) {
            c[pt][i]--;
          }
          lastcard.type = STRAIGHT;
          lastcard.straight.start = start;
          lastcard.straight.end = end;
          lastcard.straight.length = end - start + 1;
          goto lastcp;
          next_beginning:;
        }
      }
      int three_times_card = 0;
      rep(i, 1, 13) {
        if (c[pt][i] >= 3) {
          c[pt][i] -= 3;
          three_times_card = i;
          break;
        }
      }
      if (three_times_card) {
        lastcard.triple.carda = three_times_card;
        rep(i, 1, 13) {
          if (c[pt][i] >= 2 && pt != three_times_card) {
            lastcard.type = TRIPLE_T;
            lastcard.triple.cardb = i;
            c[pt][i] -= 2;
            goto lastcp;
          }
        } 
        rep(i, 1, 13) {
          if (c[pt][i]) {
            if (pt == three_times_card) {
              if (c[pt][i] < 4) {
                continue;
              }
            }
            lastcard.type = TRIPLE_O;
            lastcard.triple.cardb = i;
            c[pt][i]--;
            goto lastcp;
          }
        }
      }
      rep(i, 1, 13) {
        if (c[pt][i] >= 2) {
          lastcard.type = COUPLE;
          lastcard.couple.card = i;
          c[pt][i] -= 2;
          goto lastcp;
        }
      }
      rep(i, 1, 15) {
        if (c[pt][i]) {
          lastcard.type = SINGLE;
          lastcard.single.card = i;
          c[pt][i]--;
          goto lastcp;
        }
      }
    } else {
      if (pt == lastcp_pt) {
        flg = 1;
        continue;
      }
      if (lastcard.type == SINGLE) {
        rep(i, lastcard.single.card + 1, 15) {
          if (c[pt][i]) {
            c[pt][i]--;
            lastcard.single.card = i;
            lastcp_pt = pt;
            goto lastcp;
          }
        }
      }
      if (lastcard.type == COUPLE) {
        rep(i, lastcard.couple.card + 1, 13) {
          if (c[pt][i] >= 2) {
            c[pt][i] -= 2;
            lastcp_pt = pt;
            lastcard.couple.card = i;
            goto lastcp;
          }
        }
      }
      int three_times_card = 0;
      rep(i, lastcard.triple.carda + 1, 13) {
        if (c[pt][i] >= 3) {
          three_times_card = i;
          break;
        }
      }
      if (lastcard.type == TRIPLE_T) {
        int two_times_card = 0;
        rep(i, 1, 13) {
          if (i == three_times_card) continue;
          if (c[pt][i] >= 2) {
            two_times_card = i;
            break;
          }
        }
        if (three_times_card && two_times_card) {
          c[pt][three_times_card] -= 3;
          c[pt][two_times_card] -= 2;
          lastcard.triple.cardb = two_times_card;
          lastcard.triple.carda = three_times_card;
          lastcp_pt = pt;
          goto lastcp;
        }
        rep(i, 1, 13) {
          if (c[pt][i] == 4) {
            c[pt][i] -= 4;
            lastcard.type = BOMB;
            lastcard.bomb.card = i;
            lastcp_pt = pt;
            goto lastcp;
          }
        }   
        if (c[pt][14] && c[pt][15]) {
          c[pt][14]--;
          c[pt][15]--;
          lastcard.type = DOUBLE_KING;
          lastcp_pt = pt;
        }
      }
      if  (lastcard.type == TRIPLE_O) {
        if (!three_times_card) continue;
        rep(i, 1, 13) {
          if (i == three_times_card) {
            if (c[pt][i] < 4) continue;
          }
          if (c[pt][i]) {
            c[pt][i]--;
            lastcp_pt = pt;
            c[pt][three_times_card] -= 3;
            lastcard.triple.cardb = i;
            goto lastcp;
          }
          rep(i, 1, 13) {
            if (c[pt][i] == 4) {
              c[pt][i] -= 4;
              lastcard.type = BOMB;
              lastcard.bomb.card = i;
              lastcp_pt = pt;
              goto lastcp;
            }
          }
          if (c[pt][14] && c[pt][15]) {
            c[pt][14]--;
            c[pt][15]--;
            lastcard.type = DOUBLE_KING;
            lastcp_pt = pt;
            goto lastcp;
          }
        }
      }
      if (lastcard.type == BOMB) {
        rep(i, lastcard.bomb.card + 1, 13) {
          if (c[pt][i] == 4) {
            c[pt][i] -= 4;
            lastcard.type = BOMB;
            lastcp_pt = pt;
            goto lastcp;
          }
        }
        if (c[pt][14] && c[pt][15]) {
          c[pt][14]--;
          c[pt][15]--;
          lastcard.type = DOUBLE_KING;
          lastcp_pt = pt;
        }
      }
      if (lastcard.type == PLANE) {
        rep(i, lastcard.plane.start + 1, 12) {
          int e = lastcard.plane.length + i + 1;
          rep(k, i, e) {
            if (c[pt][k] < lastcard.plane.times) {
              goto repp;
            }
          }
          rep(k, i, e) {
            c[pt][k] -= lastcard.plane.times;
          }
          lastcp_pt = pt;
          lastcard.plane.start = i;
          lastcard.plane.end = e;
          goto lastcp;
          repp:;

        }
        rep(i, 1, 13) {
          if (c[pt][i] == 4) {
            c[pt][i] -= 4;
            lastcard.type = BOMB;
            lastcard.bomb.card = i;
            lastcp_pt = pt;
            goto lastcp;
          }
        }
        if (c[pt][14] && c[pt][15]) {
          c[pt][14]--;
          c[pt][15]--;
          lastcard.type = DOUBLE_KING;
          lastcp_pt = pt;
          goto  lastcp;
        }
      }
      if (lastcard.type == STRAIGHT) {
        rep(i, lastcard.straight.start + 1, 12) {
          int e = lastcard.straight.length + i;
          rep(k, i, e) {
            if (c[pt][k] < 1) {
              goto nerepp;
            }
          }
          rep(k, i, e) {
            c[pt][k]--;
          }
          lastcp_pt = pt;
          lastcard.straight.start = i;
          lastcard.straight.end = e;
          goto lastcp;
          nerepp:;
        }
        rep(i, 1, 13) {
          if (c[pt][i] == 4) {
            c[pt][i] -= 4;
            lastcard.type = BOMB;
            lastcard.bomb.card = i;
            lastcp_pt = pt;
            goto lastcp;
          }
        }
        if (c[pt][14] && c[pt][15]) {
          c[pt][14]--;
          c[pt][15]--;
          lastcard.type = DOUBLE_KING;
          lastcp_pt = pt;
          goto lastcp;
        }
      } 
    }
    lastcp:;
    if (flg) flg = 0;
    pt++;
    if (pt > n) pt = 1;
  }
  return 0;
}

作者:2044-space-elevator

出处:https://www.cnblogs.com/2044-space-elevator/articles/18388518

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   CoutingStars  阅读(22)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示