CSP-S 2023 题解
$\mathbb{T}$ $1$
简单爆搜即可
#include<bits/stdc++.h>
using namespace std;
const int N = 10;
int n, ans;
int st[N], ce[N][10];
int dis(int x, int y, int xb, int yb) {
int ddd = (x - xb + 10) % 10;
int dd2 = (y - yb + 10) % 10;
//cout << ddd << " " << dd2 << endl;
return (ddd == dd2);
}
int calc(int id) {
int cnt = 0;
int id1 = 0, id2 = 0;
for(int i = 1; i <= 5;i++) {
if(st[i] == ce[id][i]) continue;
if(!id1) id1 = i;
else if(!id2) id2 = i;
cnt++;
}
if(cnt == 1) return 1;
if(cnt > 2) return 0;
//cout << id1 << " " << id2 << endl;
if(id1 != id2 - 1 && id1 != id2 + 1) return 0;
for(int i = 1; i < 5;i++) {
if(st[i] != ce[id][i] && st[i + 1] != ce[id][i + 1]) {
if(!dis(st[i], st[i + 1], ce[id][i], ce[id][i + 1])) return 0;
}
}
return 1;
}
void did() {
//for(int i = 1;i <= 5;i++) cout << st[i] << " ";
//cout << endl;
for(int i = 1;i <= n;i++) {
if(!calc(i)) return;
}
ans++;
}
void dfs(int i) {
if(i > 5) return did();
for(int j = 0;j <= 9;j++) {
st[i] = j;
dfs(i + 1);
st[i] = 0;
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n;i++) {
for(int j = 1;j <= 5;j++) {
scanf("%d", &ce[i][j]);
}
}
dfs(1);
printf("%d", ans);
return 0;
}
$\mathbb{T}$ $2$
做法 $1$:
考虑分治,枚举 $[l \sim mid,mid]$ 的区间仿照括号匹配,计算哈希值放入 unordered_map 中,然后吗枚举 $[mid + 1, mid + 1 \sim r]$ 的区间,在哈希表中查询。复杂度 $O(n \log n)$ 会被卡常。得分区间 $80 \sim 95$
#include<bits/stdc++.h>
using namespace std;
#define R(i, a, b) for(int i = a;i <= b;++i)
#define F(i, a, b) for(int i = a;i >= b;--i)
typedef long long ll;
const int N = 2e6 + 500;
const ll p = 18313;
int T, n, top;
char a[N];
int st[N];
ll oc[N], ans;
unordered_map<ll, int>Q;
void solve(int l, int r) {
if(l >= r) return ;
int mid = l + r >> 1;
while(Q.size()) Q.erase(Q.begin());
top = 0;
F(i, mid, l) {
if(a[i] == a[st[top]]) top--;
else {
st[++top] = i;
oc[top] = (oc[top - 1] * p + a[i]);
}
Q[oc[top]]++;
}
top = 0;
R(i, mid + 1, r) {
if(a[i] == a[st[top]]) top--;
else {
st[++top] = i;
oc[top] = (oc[top - 1] * p + a[i]);
}
ans += Q[oc[top]];
}
solve(l, mid), solve(mid + 1, r);
}
void work() {
scanf("%d", &n);
scanf("%s", a + 1);
ans = 0, solve(1, n);
printf("%lld\n", ans);
}
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
//scanf("%d", &T);
T = 1;
while(T--) work();
return 0;
}
做法 $2$:
我们直接枚举右端点 $r$,查询满足栈状态等于 $r$ 的所有左端点 $l$。复杂度 $O(n)$。考虑正确性:有两种情况,我们分讨。
设 $l$ 的状态为 $s_1$,$ l\le k \le r$,$l \sim k$ 的状态为 $s_2$,$k \sim r$ 的状态为 $s_3$
- $s_2$ 与 $s_1$ 无法匹配,而 $r$ 的状态等于 $s_1$ 说明 $s_2$ 可以与 $s_3$ 匹配。
- $s_2$ 与 $s_1$ 可以匹配一部分,设匹配到点 $k$,所以 $s_1$ 的后半部分能与 $s_2$ 匹配,而 $r$ 的状态等于 $s_1$ 说明 $s_1$ 的后半部分等于 $s_3$,所以$s_2$ 与 $s_3$ 其实也是可以匹配的。
#include<bits/stdc++.h>
using namespace std;
#define R(i, a, b) for(int i = a;i <= b;++i)
#define F(i, a, b) for(int i = a;i >= b;--i)
typedef long long ll;
const int N = 2e6 + 500;
const ll p = 103;
//const ll M = 1e9 + 7;
int T, n, top;
char a[N];
int st[N];
ll oc[N], ans;
unordered_map<ll, int>Q;
void solve() {
Q[0]++;
R(i, 1, n) {
if(a[i] == a[st[top]]) top--;
else {
st[++top] = i;
oc[top] = (oc[top - 1] * p + (ll)a[i]);
}
ans += Q[oc[top]];
Q[oc[top]]++;
}
}
void work() {
scanf("%d", &n);
scanf("%s", a + 1);
ans = 0, solve();
printf("%lld\n", ans);
}
int main(){
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
//scanf("%d", &T);
T = 1;
while(T--) work();
return 0;
}
$\mathbb{T}$ $3$
%你题,可以开一个 根 类型的变量简化操作,取消分类讨论。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef long long ll;
const int N = 105;
struct NODE {
string NamE, TypE;
ll O, Siz;
};
struct TYPE {
vector<NODE> Ts; // name type
int Talign;
ll Tsiz;
};
map<string, TYPE> Type;
map<string, int> Mark;
int n; //Tot, , Vot
ll Addr;
void Init() {
cin >> n;
Mark["byte"] = Mark["short"] = Mark["int"] = Mark["long"] = 1;
Type["byte"].Talign = 1, Type["byte"].Tsiz = 1;
Type["short"].Talign = 2,Type["short"].Tsiz = 2;
Type["int"].Talign = 4, Type["int"].Tsiz = 4;
Type["long"].Talign = 8, Type["long"].Tsiz = 8;
}
ll Ceil(ll a, ll b) {return (a + b - 1) / b * b;}
void Build_Type() {
string Type_Name, ti, ni; int k, balign = 0;
cin >> Type_Name >> k;
ll Atd = 0;
for(int i = 1;i <= k; ++i) {
cin >> ti >> ni;
TYPE Na = Type[ti];
Atd = Ceil(Atd, Na.Talign);
Type[Type_Name].Ts.push_back(NODE{ni, ti, Atd, Na.Tsiz});
balign = max(Na.Talign, balign);
Atd += Na.Tsiz;
}
Atd = Ceil(Atd, balign);
Type[Type_Name].Tsiz = Atd;
Type[Type_Name].Talign = balign;
cout << Atd << " " << balign << "\n";
}
void Build_Vair() {
string ti, ni;
cin >> ti >> ni;
TYPE Na = Type[ti];
Addr = Ceil(Addr, Na.Talign); // 向上取整
Type[""].Ts.push_back(NODE{ni, ti, Addr, Na.Tsiz}); // 根类型
cout << Addr << "\n";
Addr += Na.Tsiz;
}
pair<ll, string> Vari_Query(string Q_TYPE, string S) { // address with type
ll res = 0;
for(auto [P, Q, R, T] : Type[Q_TYPE].Ts) { // Name Type O Siz
res = Ceil(res, Type[Q].Talign);
if(P == S) {return make_pair(res, Q);}
res = R + T;
}
return make_pair(res, "ERR");
}
void Access_Address() {
string Q_Vari, SC = ""; int Vari_Len;
cin >> Q_Vari;
Vari_Len = Q_Vari.length();
vector<string> Part;
for(int i = 0; i < Vari_Len; ++i) {
if(Q_Vari[i] == '.') {
Part.push_back(SC), SC = "";
} else SC += Q_Vari[i];
}
Part.push_back(SC);
ll addre = 0;
string Search_Type = "";
for(auto P : Part) {
pair<ll, string> S = Vari_Query(Search_Type, P);
addre += S.fi, Search_Type = S.se;
}
cout << addre << "\n";
}
string Ans = "", Link = ".";
pair<ll, pair<string, string>> Query_Type(string S, ll Requi) {
ll res = 0; //string NA, TE;
for(auto [P, Q, R, T] : Type[S].Ts) { // Ts: name type O Siz
res = Ceil(res, Type[Q].Talign);
if(res <= Requi && res + Type[Q].Tsiz > Requi) {
return make_pair(res, make_pair(P, Q));
}
res = R + T;
//NA = P, TE = Q;
}
return make_pair(res, make_pair("ERR", "ERR"));
}
bool DFS(string No_T, ll rest) {
pair<ll, pair<string, string>> Q = Query_Type(No_T, rest);
Ans += Q.se.fi + Link;
if(Mark[Q.se.se] && Q.fi + Type[Q.se.se].Tsiz >= rest) return 1;
if(Mark[Q.se.se] && Q.fi + Type[Q.se.se].Tsiz < rest) return 0;
if(Q.se.se == "ERR") return 0;
if(!DFS(Q.se.se, rest - Q.fi)) return 0;
return 1;
}
void Access_Vari() {
ll Q_Addr;
cin >> Q_Addr;
Ans = "";
if(!DFS("", Q_Addr)) {cout<<"ERR\n"; return ;};
int Ans_Len = Ans.length(); Ans[Ans_Len - 1] = '\n';
cout << Ans;
}
int main() {
Init();
for(int i = 1;i <= n; ++i) {
int ot; scanf("%d", &ot);
if(ot == 1) {Build_Type();}
else if(ot == 2) {Build_Vair();}
else if(ot == 3) {Access_Address();}
else {Access_Vari();}
}
return 0;
}
$\mathbb{T}$ $4$
先考虑 $c = 0$ 怎么做。发现有一个显然的贪心:每次沿着需要最早天数被种的点种树。考虑证明:假设当前我们需要最早的点为 $u$,另有一个天数大于它的点 $v$。如果先走 $v$ 再走 $u$ 合法:设 $u,v$ 向联通块的距离为 $dis_u,dis_v$,当前天数为 $tot$。有:
$\{\begin{matrix}tot +dis_v \le a_v\\ tot+dis_v +dis_u \le a_u\end{matrix}$,我们考虑交换顺序,这显然也是满足的,且不会更劣。 我们发现,只要知道了具体的天数,就可以按此贪心方法做。思考如何求出具体的天数:一个显然的性质是,如果我们知道最后一天,可以求出每个点至少在第几天被种下去,且答案具有单调性。这启发我们二分答案。而求具体天数可以二分,也可以 $O(1)$ 直接解。总复杂度 $O(n \log V \log b) /O(n \log V)$
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 i128;
const int N = 1e6 + 500;
struct edge{
int next, to;
} e[N << 1];
int n, cnt, top;
int head[N], fa[N], st[N], vis[N];
ll a[N];
int b[N], c[N], t[N], cot, dns;
void add(int f, int t) {
e[++cnt] = edge{head[f], t}, head[f] = cnt;
}
int calc(ll a, int b, int c, int y) {
auto f = [=](int x, int m) {
int d = 1e9;
if(c < 0) d = (b - 1) / -c; d = max(min(d, m), x - 1);
return (i128)(d - x + 1) * b + (i128)(d - x + 1) * c * (x + d) / 2 + m - d >= a;
};
int l = 0, r = min(n, y), mid;
while(l < r) {
mid = l + r + 1 >> 1;
if(f(mid, y)) l = mid;
else r = mid - 1;
}
return l;
}
void pre_dfs(int u, int f) {
fa[u] = f;
for(int i = head[u];i;i = e[i].next) {
int v = e[i].to;
if(v == f) continue;
pre_dfs(v, u);
}
}
bool check(int y) {
memset(vis, 0, sizeof vis), cot = 0;
for(int i = 1; i <= n; ++i) t[i] = calc(a[i], b[i], c[i], y);
//cout << y << " \n"; for(int i = 1;i <= n; ++i) cout << t[i] << " "; cout << endl;
vector<pair<int, int>> E;
for(int i = 1; i <= n; ++i) E.push_back(make_pair(t[i], i));
sort(E.begin(), E.end(), less<pair<int, int>>());
for(auto [Q, P] : E) {
top = 0; if(vis[P]) continue;
for(int u = P; u ; u = fa[u]) { if(vis[u]) {break;} st[++top] = u;}
for(int i = top;i >= 1; --i) {
vis[st[i]] = 1, cot++;
if(cot > t[st[i]]) return 0;
}
} //cout << endl;
return 1;
}
int main() {
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
int l = n, r = 1e9, ans;
scanf("%d", &n);
for(int i = 1;i <= n; ++i) scanf("%lld %d %d", &a[i], &b[i], &c[i]);
for(int i = 1;i < n; ++i) {
int u, v; scanf("%d %d", &u, &v);
add(u, v), add(v, u);
}
pre_dfs(1, 0);
//for(int i = 1;i <= n; ++i) cout << fa[i] << " "; cout << endl;
while(l < r) {
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
printf("%d", r);
return 0;
}

浙公网安备 33010602011771号