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号
浙公网安备 33010602011771号