2025牛客暑期多校训练营2 补题

Identical Somehow

有 1 不可能,其他情况都是 1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
#define rep(x,l,r) for(int x=l;x<=r;++x)
#define per(x,r,l) for(int x=r;x>=l;--x)
#define mk(x,y) make_pair(x,y)
#define pll pair<ll,ll>
#define pii pair<int,int>
#define max(x,y) ((x)<(y)?(y):(x))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
typedef long long ll;
typedef double db;
const ll N=420+7,mod=1e9+7;
const ll inf=1e18;
const double eps=1e-11;

void solve()
{
    ll x,y;
    cin>>x>>y;
    if(x!=1&&y!=1)  printf("1\n");
    else {
        printf("-1\n");
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T=1;
    cin>>T;
    while(T--) {
        solve();
    }
    return 0;
}

Field of Fire

找到最长的连续 0 ,看为其隔离能留下多少

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
#define rep(x,l,r) for(int x=l;x<=r;++x)
#define per(x,r,l) for(int x=r;x>=l;--x)
#define mk(x,y) make_pair(x,y)
#define pll pair<ll,ll>
#define pii pair<int,int>
#define max(x,y) ((x)<(y)?(y):(x))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
typedef long long ll;
typedef double db;
const ll N=5e5+7,mod=998244353;
const ll inf=1e18;
const double eps=1e-11;

void solve()
{
    vector<int> a;
    string s;
    int n,t,st=0,ed=0;
    cin>>n>>t>>s;
    rep(i,0,n-1){
        if(s[i]=='1') {
            st=i;
            break;
        }
    }
    per(i,n-1,0){
        if(s[i]=='1') {
            ed=i;
            break;
        }
    }
    a.push_back(st+n-1-ed);
    int last=st;
    rep(i,st+1,ed) {
        if(s[i]=='1') {
            a.push_back(i-last-1);
            last=i;
        }
    }
    sort(a.begin(), a.end(),greater<int>());
    bool flag=1;
    int ans=0;
    for(auto x:a) {
        int s=2;
        if(flag)  s=1,flag=0,--x;
        int res=max(0,x-t*s);
        ans+=res;
    }
    printf("%d\n",ans);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T=1;
    cin>>T;
    while(T--) {
        solve();
    }
    return 0;
}

Bitwise Perfect

易发现,操作 x, y 得到的 z 要严格大于 max(x, y)。

0-1 Trie 树满足解法

题解正解为,当数组长度小于 60 时,直接暴力判断,否则一定会小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <bits/stdc++.h>
#define rep(x,l,r) for(int x=l;x<=r;++x)
#define per(x,r,l) for(int x=r;x>=l;--x)
#define mk(x,y) make_pair(x,y)
#define pll pair<ll,ll>
#define pii pair<int,int>
#define max(x,y) ((x)<(y)?(y):(x))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
typedef long long ll;
typedef double db;
const ll N=5e5+7,mod=998244353;
const ll inf=1e18;
const double eps=1e-11;
int tr[N*61][2],tot=0;
ll a[N];
void add(ll x)
{
    int now=0;
    per(i,60,0) {
        int c=(x>>i)&1;
        if(!tr[now][c]) tr[now][c]=++tot;
        now=tr[now][c];
    }
}
bool check(ll x)
{
    int now=0;
    ll res=0;
    per(i,60,0) {
        int c=(x>>i)&1;
        if(tr[now][c])  now=tr[now][c];
        else {
            res+=(1LL<<i);
            now=tr[now][c^1];
        }
    }
    return res>x;
}
void solve()
{
    rep(i,0,tot) tr[i][0]=tr[i][1]=0;
    tot=0;
    int n;
    cin>>n;
    rep(i,1,n)  cin>>a[i];
    sort(a+1,a+1+n);
    add(a[1]);
    rep(i,2,n) {
        bool ans=check(a[i]);
        if(!ans) {
            printf("NO\n");
            return;
        }
        add(a[i]);
    }
    printf("YES\n");
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T=1;
    cin>>T;
    while(T--) {
        solve();
    }
    return 0;
}

Another Day of Sun

显然考虑 DP。

定义以下 DP 状态:

  • $f[i][j]$:表示所有长度为 $i$,且第 $i$ 位为 $j$($j \in {0, 1}$)的合法序列的天数总和。
  • $cnt[i][j]$:表示所有长度为 $i$,且第 $i$ 位为 $j$($j \in {0, 1}$)的合法序列的数量。

如果第 $i$ 位填 0 (当 $a_i=0$ 或 $a_i=-1$ 时):

它可以接在任何一个以 0 或 1 结尾的长度为 $i-1$ 的序列后面。 数量:$cnt[i] = (cnt[i-1] + cnt[i-1]) \pmod{mod}$。 天数总和:因为在末尾添加 0 不会产生新的天数,所以我们只是把之前的天数和继承过来。 $f[i] = (f[i-1] + f[i-1]) \pmod{mod}$。

如果第 $i$ 位填 1 (当 $a_i=1$ 或 $a_i=-1$ 时):

它也可以接在任何一个以 0 或 1 结尾的长度为 $i-1$ 的序列后面。 数量:$cnt[i] = (cnt[i-1] + cnt[i-1]) \pmod{mod}$。 天数总和:这里是关键。

如果接在以 1 结尾的序列后面(形如 …11),天数不变。这部分贡献的天数总和是 $f[i-1]$。 如果接在以 0 结尾的序列后面(形如 …01),每个这样的序列都会新增 1 天。共有 $cnt[i-1]$ 个这样的序列,所以天数总和会额外增加 $cnt[i-1]$。这部分贡献的天数总和是 $f[i-1] + cnt[i-1]$。 所以总的天数和是:$f[i] = (f[i-1] + f[i-1] + cnt[i-1]) \pmod{mod}$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <bits/stdc++.h>
#define rep(x,l,r) for(int x=l;x<=r;++x)
#define per(x,r,l) for(int x=r;x>=l;--x)
#define mk(x,y) make_pair(x,y)
#define pll pair<ll,ll>
#define pii pair<int,int>
#define max(x,y) ((x)<(y)?(y):(x))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
typedef long long ll;
typedef double db;
const ll N=5e5+7,mod=998244353;
const ll inf=1e18;
const double eps=1e-11;
int f[N][2],a[N],cnt[N][2];
void solve()
{
    int n;
    cin>>n;
    rep(i,1,n)  cin>>a[i];
    f[0][0]=f[0][1]=0;
    cnt[0][0]=1;
    cnt[0][1]=0;
    rep(i,1,n) {
        rep(j,0,1) {
            if(!(a[i]==-1||a[i]==j))  continue;
            f[i][j]=(f[i-1][0]+f[i-1][1])%mod;
            if(j)  f[i][j]=(f[i][j]+cnt[i-1][0])%mod;
            cnt[i][j]=(cnt[i-1][0]+cnt[i-1][1])%mod;
        }
    }
    printf("%lld\n",(f[n][0]+f[n][1])%mod);
    rep(i,1,n)  f[i][0]=f[i][1]=cnt[i][0]=cnt[i][1]=0;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T=1;
    cin>>T;
    while(T--) {
        solve();
    }
    return 0;
}

Love Wins All

根据题意,可以推断出,所有居民会构成若干个偶数长度的环,0 个或 2 个的奇数长度环。

需要拆出两个人:

  • 如果有奇数环,两个环中各拆一个,每个长度大于 2 的偶数环贡献 2 种可能。

  • 如果没有奇数环,一定是在一个偶数环中拆:固定一个人 i,另一人向右看,只能是 i + 1 + 2 * n 位置上的,一共有 $\frac{L_i \times (L_i/2)}{2} = \frac{L_i^2}{4}$ 种,当前这个环剩下的只能贡献 1,其他偶数环正常贡献。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <bits/stdc++.h>
#define rep(x,l,r) for(int x=l;x<=r;++x)
#define per(x,r,l) for(int x=r;x>=l;--x)
#define mk(x,y) make_pair(x,y)
#define pll pair<ll,ll>
#define pii pair<int,int>
#define max(x,y) ((x)<(y)?(y):(x))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
typedef long long ll;
typedef double db;
const ll N=5e5+7,mod=998244353;
const ll inf=1e18;
const double eps=1e-11;
int a[N],cnt=0;
vector<ll> c[2];
ll inv[3];
bool vis[N];
ll qpow(ll x,ll k)
{
    ll res=1;
    while(k) {
        if(k&1) res=res*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return res;
}
void dfs(int x)
{
    if(vis[x]) {
        c[cnt&1].push_back(cnt);
        return;
    }
    vis[x]=1;
    ++cnt;
    dfs(a[x]);
}
void solve()
{
    c[0].clear();
    c[1].clear();
    int n;
    cin>>n;
    rep(i,1,n) {
        cin>>a[i];
    }
    rep(i,1,n) {
        cnt=0;
        if(!vis[i])  dfs(i);
    }
    rep(i,1,n) {
        vis[i]=0;
    }

    if(c[1].size()>2) {
        printf("0\n");
        return ;
    }
    ll ans=0;
    if(c[1].size()==2) {
        ans=c[1][0]*c[1][1]%mod;
        for(auto x:c[0]) {
            if(x>2)  ans=ans*2%mod;
        }
    } else {
        ans=0;
        ll res=1;
        for(auto x:c[0]) {
            if(x>2)  res=res*2%mod;
        }
        for(auto x:c[0]) {
            ll tmp=res;
            if(x>2)  tmp=tmp*inv[2]%mod;
            ll y=(x/2)*(x/2);
            ans=(ans+y*tmp%mod)%mod;
        }
    }
    printf("%lld\n",ans);
    
}
signed main()
{
    inv[1]=1;
    inv[2]=qpow(2,mod-2);
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T=1;
    cin>>T;
    while(T--) {
        solve();
    }
    return 0;
}

Geometry Friend

太寄把糖了。

当中心点在多边形内时,只要离他最远的点集能覆盖满 360 即可。

当中心点在多边形外时,构成圆环,此时一定要转够 360。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 1e5 + 7, mod = 1e9 + 7;
const double PI = acos(-1.0);

struct Node {
    ll x, y;
    ll dot(const Node &a) const {
        return x * a.x + y * a.y;
    }
    ll cross(const Node &a) const {
        return x * a.y - y * a.x;
    }
};

void solve() {
    int n;
    Node o;
    cin >> n >> o.x >> o.y;
    vector<Node> p(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> p[i].x >> p[i].y;
        p[i].x -= o.x;
        p[i].y -= o.y;
    }
    p[0] = p[n];
    bool flag = 0;
    for (int i = 1; i <= n; i++) {
        if (p[i].cross(p[i - 1]) > 0) {
            flag = 1;
            break;
        }
    }
    if (flag) {
        printf("%.10lf\n", 2 * PI);
        return;
    }
    vector<double> ang;
    ll mx = 0;
    for (int i = 1; i <= n; i++) {
        mx = max(mx, p[i].dot(p[i]));
    }
    for (int i = 1; i <= n; i++) {
        if (p[i].dot(p[i]) == mx) {
            ang.push_back(atan2(p[i].y, p[i].x));
        }
    }
    sort(ang.begin(), ang.end());
    ang.push_back(ang[0] + 2 * PI);
    double ans = 0;
    for (int i = 1; i < ang.size(); i++) {
        ans = max(ans, ang[i] - ang[i - 1]);
    }
    printf("%.10lf\n", ans);
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

Highway Upgrade

观察到题目中提到 $t_j - w_j * k_i > 0$,说明操作一定是集中在一条路上。

那么操作的路的前后都是固定的,可以通过预处理提前得到,对起点终点分别跑 dijkstra,即可得到两侧的 dist。

注意到此时每条路对应的代价 $L_i(k) = \text{dist}(1, u_i) + (t_i - k \cdot w_i) + \text{dist}(v_i, n)$

令 $C_i = \text{dist}(1, u_i) + t_i + \text{dist}(v_i, n)$,那么 $L_i(k) = C_i - w_i \cdot k$。

此时问题转化为 $\min_{i=1 \dots m} { C_i - w_i \cdot k }$,这是一个经典的“查询直线集合在某点上的最小值”问题,每一条边 $i$ 都对应一条直线 $y = -w_i \cdot k + C_i$。

可以通过凸包优化。

把这些直线 $y = C_i - w_i \cdot k$ 画在以 k 为横坐标,L(k) 为纵坐标的平面上,对于任何一个 k 值(一条竖直线),我们要求的最小值,就是这条竖直线与所有 m 条直线交点中,最下方的那一个。把所有这些最下方的点连起来,就形成了一个下凸壳(Lower Convex Hull)。

我们将线段按照截距升序(如果截距相同,按斜率降序)排列。

通过单调栈完成操作。

遍历所有直线,如果当前直线的斜率比栈中最后一个元素的斜率大,那么他下降的就会比已有的线段慢,因为新直连的截距一定比旧的大,这条直线就不再考虑。

反之,找到找到当前直线比旧线低的位置,如果这个位置比旧线战胜他之前的线的位置更早,就踢出旧线,直到无法踢出,或栈为空。

现在这个单调栈中储存的就是我们要的下凸壳。

我们离线所有查询,排序后对下凸壳查询即可。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 1e5 + 7, mod = 1e9 + 7;

struct Node {
    int v;
    ll w;
    bool operator<(const Node &a) const {
        return w > a.w; 
    }
};

auto dijkstra(int s, int n, vector<Node> e[]) {
    vector<ll> dis(n + 1, -1);
    priority_queue<pair<int, ll>, vector<pair<int, ll>>, greater<pair<int, ll>>> pq;
    dis.assign(n + 1, LLONG_MAX);
    dis[s] = 0;
    pq.push({s, 0});
    while (!pq.empty()) {
        auto [u, d] = pq.top();
        pq.pop();
        if (d > dis[u]) continue;
        for (auto &v : e[u]) {
            if (dis[v.v] > d + v.w) {
                dis[v.v] = d + v.w;
                pq.push({v.v, dis[v.v]});
            }
        }
    }
    return dis;
}

void solve() {
    int n, m;
    cin >> n >> m;
    vector<Node> e[2][n + 1];
    vector<array<ll, 4>> a(m + 1);
    for (int i = 1; i <= m; i++) {
        int u, v;
        ll t, w;
        cin >> u >> v >> t >> w;
        a[i] = {u, v, t, w};
        e[0][u].push_back({v, t});
        e[1][v].push_back({u, t});
    }
    auto dis0 = dijkstra(1, n, e[0]);
    auto dis1 = dijkstra(n, n, e[1]);
    vector<pair<ll, ll>> line;
    for (int i = 1; i <= m; i++) {
        auto [u, v, t, w] = a[i];
        if (dis0[u] == LLONG_MAX || dis1[v] == LLONG_MAX) {
            continue;
        }
        ll tmp = dis0[u] + t + dis1[v];
        line.push_back({tmp, w});
    }
    sort(line.begin(), line.end());
    deque<pair<pair<ll, ll>, ll>> q;
    for (auto [x, y] : line) {
        if (!q.empty() && q.back().first.second >= y) continue;
        ll tmp = 0;
        while (!q.empty()) {
            tmp = ceil((double)(x - q.back().first.first) / (y - q.back().first.second));
            if (tmp <= q.back().second) {
                q.pop_back();
            } else {
                break;
            }
        }
        if (q.empty()) q.push_back({{x, y}, 0});
        else q.push_back({{x, y}, tmp});
    }
    int qq;
    cin >> qq;
    vector<pair<ll, ll>> ask;
    vector<ll> ans(qq + 1);
    for (int i = 1; i <= qq; i++) {
        int x;
        cin >> x;
        ask.push_back({x, i});
    }
    sort(ask.begin(), ask.end());
    for (auto [x, i] : ask) {
        while (q.size() > 1 && q[1].second <= x) {
            q.pop_front();
        }
        ans[i] = q[0].first.first - q[0].first.second * x;
    }
    for (int i = 1; i <= qq; i++) {
        cout << ans[i] << endl;
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}

Donkey Thinks…

题目要求

$$\sum h_i - (V - \sum s_i)(\sum d_i)$$

显然难以直接实现,注意到 V 的范围极小,只到 500,所以可以枚举 $(V - \sum s_i)$ 部分中的 $S=\sum s_i$,此时,背包中的每个物品的价值都变成了 $h_i-(V-S)d_i$,此时即可进行常规背包。

但是,完全遍历每一个物品,时间复杂度来到了 $O(V * N log N)$,显然面临 TLE 的局面,不难想到,对于每种体积 s 的物品,我们只会在价值前 $k = \left\lfloor\frac{S}{s}\right\rfloor$ 大的物品中背包。

这里使用 nth_element 进行优化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long ll;

const int N = 1e5 + 7, mod = 1e9 + 7;

void solve() {
    int n, v;
    cin >> n >> v;
    vector<ll> h(n + 1), s(n + 1), d(n + 1);
    for (int i =1; i <= n; i++) {
        cin >> h[i] >> s[i] >> d[i];
    }
    ll ans = 0;
    for (int i = 1; i <= v; i++) {
        vector<ll> dp(501, -1e18);
        vector<vector<ll>> val(501);
        dp[0] = 0;
        int lft = v - i;
        for (int j = 1; j <= n; j++) val[s[j]].emplace_back(h[j] - d[j] * lft);
        for (int j = 1; j <= i; j++) {
            int k = min((int)val[j].size(), i / j);
            nth_element(val[j].begin(), val[j].begin() + k - 1, val[j].end(), greater<ll>());
            for (int l = 0; l < k; l++) {
                for (int r = i; r >= j; r--) {
                    dp[r] = max(dp[r], dp[r - j] + val[j][l]);
                }
            }
        }
        ans = max(ans, dp[i]);
    }
    cout << ans << endl;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) solve();
    return 0;
}
Licensed under CC BY-NC-SA 4.0
最后更新于 Sep 11, 2025 16:27 +0800
发表了95篇文章 · 总计15万2千字
永远相信美好的事情即将发生。
使用 Hugo 构建
主题 StackJimmy 设计