可能是史上最弱的验题人——
Problem A
(小)模拟。
#include <bits/stdc++.h>
using namespace std;
int T;
int main(){
scanf("%d", &T);
while (T--){
char rk[10];
scanf("%s", rk);
int n = strlen(rk);
for (int i = 0; i < 3 - n; ++i)
putchar(' ');
printf("%s", rk);
putchar('|');
char S[20];
scanf("%s", S);
printf("%s", S);
n = strlen(S);
for (int i = n; i < 16; ++i)
putchar(' ');
putchar('|');
scanf("%s", rk);
printf("%s", rk);
scanf("%s", rk);
if (rk[0] == 'R' && rk[1] == 'u') {
int x;
scanf("%d", &x);
putchar('|');
putchar('[');
for (int i = 1; i <= x; ++i)
putchar('X');
for (int i = 1; i <= 10 - x; ++i)
putchar(' ');
putchar(']');
}
else{
if (rk[0] == 'F') {
rk[0] = 'A';
rk[1] = 'C';
rk[2] = '*';
rk[3] = '\0';
}
putchar('|');
putchar('[');
for (int i = 0; i < 4; ++i)
putchar(' ');
printf("%s", rk);
int n = strlen(rk);
for (int i = 1; i <= 6 - n; ++i)
putchar(' ');
putchar(']');
}
puts("");
}
}
Problem B
观察到$d$大于$316$的质因子最多只有一个,那么先判掉$<= 316$的所有质因子,搞个前缀和就可以了。
然后特判大于$316$的质因子即可,方法有很多。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define fi first
#define se second
#define MP make_pair
typedef long long LL;
typedef pair <int, int> PII;
const int N = 1e5 + 10;
int T;
int s[70][N];
int a[N];
int n, m;
int re[N];
int c[N];
int id = 0;
int prime[N], fp[N], val[N];
vector <PII> pri[N];
vector <int> v[N];
int main(){
rep(i, 2, 1e5){
for (int j = i + i; j <= 1e5; j += i) c[j] = 1;
}
rep(i, 2, 1e5){
if (!c[i]){
++id;
prime[id] = i;
fp[i] = id;
}
}
rep(i, 1, 1e5) val[i] = i;
rep(i, 2, 1e5) if (!c[i]){
for (int j = i + i; j <= 1e5; j += i){
int cnt = 0;
while (val[j] % i == 0) val[j] /= i, ++cnt;
pri[j].push_back(MP(i, cnt));
if (i > 316) re[j] = i;
}
}
rep(i, 2, 1e5) if (val[i] > 1){
pri[i].push_back(MP(i, 1));
if (val[i] > 316) re[i] = val[i];
}
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
memset(s, 0, sizeof s);
memset(a, 0, sizeof a);
rep(i, 1, 1e5 + 1) v[i].clear();
rep(i, 1, n){
int x;
scanf("%d", &x);
for (auto u : pri[x]){
int nowid = fp[u.fi], nowcnt = u.se;
if (u.fi <= 316){
s[nowid][i] += nowcnt;
}
}
if (re[x]){
a[i] = re[x];
v[re[x]].push_back(i);
}
}
rep(i, 1, 67){
rep(j, 1, n) s[i][j] += s[i][j - 1];
}
while (m--){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
int ret = 1;
for (auto u : pri[z]){
if (u.fi > 316) break;
int nowid = fp[u.fi], nowcnt = u.se;
ret &= (s[nowid][y] - s[nowid][x - 1] >= u.se);
}
if (re[z]){
int num = re[z];
int sz = (int)v[num].size();
if (sz == 0) ret = 0;
else{
int l = 0, r = sz - 1;
if (v[num][r] < x){
ret = 0;
}
else{
while (l + 1 < r){
int mid = (l + r) >> 1;
if (v[num][mid] >= x){
r = mid;
}
else{
l = mid + 1;
}
}
int t;
if (v[num][l] >= x) t = l;
else t = r;
int minpos = v[num][t];
ret &= (minpos <= y);
}
}
}
puts(ret ? "Yes" : "No");
}
}
return 0;
}
Problem C
二分一下就可以了,具体实现的时候要尽量避开log函数,否则会因为精度问题产生误差。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll LOG(ll x){
ll ans = 0;
while (true) {
if (x == 1) return ans;
else if (x == 2) return ans + 1;
x = x - (x / 2);
ans++;
}
}
bool check(ll n, ll k, int a, int b) {
ll m = LOG(n);
if (m == 0) return 1;
ll tmp = 1;
while (a--) {
if (k / tmp < n) return 0;
tmp *= n;
}
while (b--) {
if (k / tmp < m) return 0;
tmp *= m;
}
return 1;
}
int a, b;
int T;
ll k;
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d%I64d", &a, &b, &k);
ll l = 1, r = k, ans;
while (l <= r) {
ll mid = (l + r) / 2;
if (check(mid, k, a, b)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%I64d\n", ans);
}
return 0;
}
Problem D
难题。考虑DP,设$f[i][j][x][y]$表示走到第$i$行第$j$列,有$x$个路径上本来应该计入答案的格子没有计入答案,
有$y$个本不属于这条路径的格子计入了答案的最大值。
走到$a_{i,j}$的时候把那些肯定不可能走到的格子排序然后贪心转移即可。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 53;
const int K = 27;
int T;
int f[N][N][K][K], a[N][N], c[N];
int n, m, k;
int ans = 0;
inline void up(int &x, int y){
if (x < y) x = y;
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d%d", &n, &m, &k);
rep(i, 1, n){
rep(j, 1, m) scanf("%d", a[i] + j);
}
memset(f, 0xc0, sizeof f);
f[1][1][0][0] = a[1][1];
f[1][1][0][1] = 0;
rep(i, 1, n){
rep(j, 1, m){
rep(q, 1, m){
if (q != j){
if (q < j){
c[q] = a[i + 1][q];
}
else{
c[q] = a[i][q];
}
}
else{
c[q] = 0;
}
}
sort(c + 1, c + m + 1, greater<int>());
rep(q, 1, m){
c[q] += c[q - 1];
}
rep(p, 0, k){
rep(q, 0, k){
up(f[i][j + 1][p][q], f[i][j][p][q] + a[i][j + 1]);
up(f[i][j + 1][p][q + 1], f[i][j][p][q]);
rep(z, 0, m - 1){
if (p + z > k) break;
up(f[i + 1][j][p + z][q], f[i][j][p][q] + a[i + 1][j] + c[z]);
up(f[i + 1][j][p + z][q + 1], f[i][j][p][q] + c[z]);
}
}
}
}
}
ans = 0;
rep(i, 0, k) up(ans, f[n][m][i][i]);
printf("%d\n", ans);
}
return 0;
}
Problem E
直接上最短路就可以了,注意计算对数要用整数运算,跑最短路要堆优化Dij
(不是这个算法的全被卡了)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair <int, int> PII;
const int N = 2e5 + 10;
struct node{
int u;
LL w;
friend bool operator < (const node &a, const node &b){
return a.w > b.w;
}
};
int T;
int b[N];
int inqueue[N];
int n, m;
int ret;
LL d[N];
LL a[N];
vector <PII> v[N];
void dij(){
priority_queue <node> q;
static bool vis[N];
rep(i, 1, n + 1) d[i] = 4e18, vis[i] = false;
q.push({1, 1});
d[1] = 1;
while (!q.empty()){
int u = q.top().u;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto edge : v[u]){
int xx = edge.fi, id = edge.se, wb = b[id];
LL wa = a[id];
if (d[u] + wa < d[xx] && (d[u] + wa) / d[u] >= (1ll << wb)){
d[xx] = d[u] + wa;
q.push({xx, d[xx]});
}
}
}
}
int main(){
scanf("%d", &T);
while (T--){
rep(i, 0, n + 1) v[i].clear();
scanf("%d%d", &n, &m);
rep(i, 1, m){
int x, y;
scanf("%d%d%lld%d", &x, &y, a + i, b + i);
v[x].push_back(MP(y, i));
}
dij();
if (d[n] == 4e18){
puts("-1");
continue;
}
ret = 0;
while (d[n]){
++ret;
d[n] /= 2ll;
}
printf("%d\n", ret - 1);
}
return 0;
}
Problem F
勇敢地直接上树上莫队,对$01$数组分块,同时维护每个块内$0$的个数,然后注意一些细节,就可以AC了。
在HDOJ上大概要跑$10s$。
二分答案+主席树方法待补。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
const int N = 2e5 + 10;
const int A = 19;
int T;
int n, m;
int cnt, bs, g, top, ti, ret;
int a[N], b[N], c[N], bg[N];
int f[N][A], deep[N], dfn[N];
int belong[N], stk[N];
int vis[N], s[N], ans[N];
int L[N], R[N];
int bl[N], bss, blocknum;
int extra;
int exist[N];
vector <int> v[N];
struct node{
int x, y, id;
friend bool operator < (const node &a, const node &b){
return belong[a.x] == belong[b.x] ? dfn[a.y] < dfn[b.y] : belong[a.x] < belong[b.x];
}
} q[N];
int LCA(int a, int b){
if (deep[a] < deep[b]) swap(a, b);
for (int i = 0, delta = deep[a] - deep[b]; delta; delta >>= 1, ++i)
if (delta & 1) a = f[a][i];
if (a == b) return a;
dec(i, 18, 0) if (f[a][i] ^ f[b][i]){
a = f[a][i];
b = f[b][i];
}
return f[a][0];
}
void dfs(int x, int fa, int dep){
deep[x] = dep;
if (fa){
f[x][0] = fa;
for (int i = 0; f[f[x][i]][i]; ++i)
f[x][i + 1] = f[f[x][i]][i];
}
for (auto u : v[x]){
if (u == fa) continue;
dfs(u, x, dep + 1);
}
}
void dfs(int x, int fa){
dfn[x] = ++ti;
int bot = top;
for (auto u : v[x]){
if (u == fa) continue;
dfs(u, x);
if (top - bot >= bs){
++g;
while (top ^ bot) belong[stk[top--]] = g;
}
}
stk[++top] = x;
}
void rev(int x){
if (c[a[x]] == 0) --bg[bl[a[x]]];
else ++bg[bl[a[x]]];
c[a[x]] ^= 1;
vis[x] ^= 1;
}
void work(int x, int y){
for (; x ^ y; ){
if (deep[x] < deep[y]) swap(x, y);
rev(x);
x = f[x][0];
}
}
void init(){
rep(i, 0, n + 1) v[i].clear();
memset(c, 0, sizeof c);
memset(f, 0, sizeof f);
memset(vis, 0, sizeof vis);
memset(exist, 0, sizeof exist);
ti = 0;
top = 0;
g = 0;
cnt = 0;
bs = 0;
ret = 0;
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
init();
rep(i, 1, n) scanf("%d", a + i), b[i] = a[i];
rep(i, 1, n) exist[a[i]] = 1;
rep(i, 1, 200002) if (!exist[i]){
extra = i;
break;
}
sort(b + 1, b + n + 1);
cnt = unique(b + 1, b + n + 1) - b - 1;
rep(i, 1, n) a[i] = lower_bound(b + 1, b + cnt + 1, a[i]) - b;
rep(i, 2, n){
int x, y;
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1, 0, 0);
bs = sqrt(n);
bss = sqrt(cnt);
dfs(1, 0);
rep(i, 1, cnt) bl[i] = (i - 1) / bss + 1;
blocknum = bl[cnt];
rep(i, 1, blocknum) L[i] = 1e9, R[i] = 0;
rep(i, 1, cnt){
L[bl[i]] = min(L[bl[i]], i);
R[bl[i]] = max(R[bl[i]], i);
}
rep(i, 1, blocknum) bg[i] = R[i] - L[i] + 1;
rep(i, 1, m){
scanf("%d%d", &q[i].x, &q[i].y);
q[i].id = i;
if (dfn[q[i].x] > dfn[q[i].y]) swap(q[i].x, q[i].y);
}
sort(q + 1, q + m + 1);
q[0].x = q[0].y = 1;
rep(i, 1, m){
work(q[i - 1].x, q[i].x);
work(q[i - 1].y, q[i].y);
int lca = LCA(q[i].x, q[i].y);
rev(lca);
int now = 0, fg = 0, ret = 200001;
rep(i, 1, blocknum){
now += bg[i];
if (now > 0){
fg = 1;
rep(j, L[i], R[i]) if (!c[j]){
ret = j;
break;
}
break;
}
}
if (fg) ans[q[i].id] = min(b[ret], extra);
else ans[q[i].id] = extra;
rev(lca);
}
rep(i, 1, m) printf("%d\n", ans[i]);
}
return 0;
}
Problem G
全场题。
#include <bits/stdc++.h>
using namespace std;
const int N = 505, INF = 0x3f3f3f3f;
int t;
int main(){
scanf("%d",&t);
for (int id=1001;id<=1000+t;id++){
int n,m; scanf("%d%d",&n,&m);
int ans1=INF,ans2=INF;
for (int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
ans1=min(ans1,x);
}
for (int i=1;i<=m;i++)
{
int x; scanf("%d",&x);
ans2=min(ans2,x);
}
printf("Problem %d:\n",id);
printf("Shortest judge solution: %d bytes.\n",ans1);
if (ans2==INF)
printf("Shortest team solution: N/A bytes.\n");
else
printf("Shortest team solution: %d bytes.\n",ans2);
}
}
Problem H
难题,题目给定的是一个二分图模型,
我们需要把这个二分图的模型转化成基环生成树森林。
然后从高位到低位分治,把当前集合根据最高位$0$或$1$分成两个不同的集合。
显然集合内部连边方案更优。可以证明当两个集合大小都超过$3$时,没有横跨集合的边。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 3e5 + 10;
int T;
int n;
LL solve(int d, vector<int> a){
if (d < 0) return 0;
int n = (int)a.size();
if (n <= 4){
LL ret = 0;
vector <int> c;
rep(i, 0, n - 1) rep(j, i + 1, n - 1) c.push_back(a[i] ^ a[j]);
sort(c.begin(), c.end());
for (int i = 0; i < n && i < n * (n - 1) / 2; i++) ret += c[i];
return ret;
}
vector <int> b[2];
rep(i, 0, n - 1) b[a[i] >> d & 1].push_back(a[i]);
a.clear();
LL ret = solve(d - 1, b[0]) + solve(d - 1, b[1]);
int t[2] = {(int)b[0].size(), (int)b[1].size()};
if (t[0] && t[1] && (t[0] < 3 || t[1] < 3)){
int mi = 2e9;
rep(i, 0, t[0] - 1) rep(j, 0, t[1] - 1) mi = min(mi, b[0][i] ^ b[1][j]);
ret += mi;
}
return ret;
}
int main(){
scanf("%d", &T);
while (T--){
scanf("%d", &n);
vector <int> a(n);
rep(i, 0, n - 1) scanf("%d", &a[i]);
printf("%lld\n", solve(29, a));
}
return 0;
}
Problem I
其实这题就没几行……完全不需要后缀数组。
直接从后往前扫一遍,递推就可以了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 1e6 + 10;
int T;
int n;
char s[N], ans[N];
int main(){
scanf("%d", &T);
while (T--){
scanf("%d", &n);
scanf("%s", s + 1);
ans[n] = '>';
dec(i, n - 1, 1){
if (s[i] < s[i + 1]) ans[i] = '<';
else if (s[i] > s[i + 1]) ans[i] = '>';
else ans[i] = ans[i + 1];
}
ans[n] = '\0';
puts(ans + 1);
}
return 0;
}
Problem J
由于数据是随机的,所以直接枚举回文重心往两边不断延伸(也就是直接暴力)就可以了。
VP的时候直接尝试只枚举长度 $<= 3$的回文串,直接AC了。
赛前预计AC $≈$ $30$,赛场上去掉打星队AC $= 3$
……
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
vector<int>link[N];
int a[N];
int cnt[N];
int t;
int main(){
scanf("%d",&t);
while(t--)
{
int n; scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
ll ans=n;
for (int i=1;i<n;i++)
{
int x,y; scanf("%d%d",&x,&y);
link[x].push_back(y);
link[y].push_back(x);
if (a[x]==a[y]) ans++;
}
for (int i=1;i<=n;i++)
{
for (int v:link[i])
{
ans+=cnt[a[v]];
cnt[a[v]]++;
}
for (int v:link[i]) cnt[a[v]]--;
}
printf("%lld\n",ans);
for (int i=1;i<=n;i++) link[i].clear();
}
}
Problem K
难题。考虑容斥。
计数的时候对于某个格子,如何减掉重复计算的答案?
把这个矩阵往左,往上,往左上各推一个单位,就可以很巧妙地容斥了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
#define MP make_pair
#define fi first
#define se second
typedef long long LL;
const int N = 100005;
const int M = 1005;
inline LL calc(int n){ return 1ll * n * (n - 1) * (n - 2) / 6;}
int T;
int n;
int x[N][2], y[N][2];
int c[M][M];
LL ret[4];
int main(){
scanf("%d", &T);
while (T--){
scanf("%d", &n);
rep(i, 1, n) rep(j, 0, 1) scanf("%d%d", x[i] + j, y[i] + j);
memset(ret, 0, sizeof ret);
rep(op, 0, 3){
memset(c, 0, sizeof c);
rep(i, 1, n){
int sx = -(op / 2), sy = -(op % 2);
c[x[i][0]][y[i][0]]++;
c[x[i][0]][y[i][1] + sy + 1]--;
c[x[i][1] + sx + 1][y[i][0]]--;
c[x[i][1] + sx + 1][y[i][1] + sy + 1]++;
}
rep(i, 1, M - 1){
rep(j, 1, M - 1){
c[i][j] += c[i - 1][j] + c[i][j - 1] - c[i - 1][j - 1];
ret[op] += calc(c[i][j]);
}
}
}
printf("%lld\n", ret[0] - ret[1] - ret[2] + ret[3]);
}
return 0;
}