A. 约瑟夫问题

  • 还是很简单的,用链表直接维护就好了
    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
    signed main() {
    IOS;
    int n, m;
    cin >> n >> m;
    vector<int> P(n + 1), RE(n + 1);
    P[0] = 1;
    for (int i = 1; i <= n; i++)
    {
    P[i] = i + 1;
    RE[i] = i - 1;
    if (i == 1)
    {
    RE[i] = n;
    }
    if (i == n)
    {
    P[i] = 1;
    }
    }
    int idx = 0;
    for (int i = 1; i <= n; i++)
    {
    int op = m;
    while (op--) {
    idx = P[idx];
    }
    cout << idx << ' ';
    P[RE[idx]] = P[idx];
    RE[P[idx]] = RE[idx];
    }
    return 0;
    }

B. 第K大和

用链表可以轻松维护区间

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
struct node {
int pre, end, val;
};
void solve()
{
int n, m;
cin >> n >> m;
vector<node> v(n + 2);
vector<int> P(n + 2);
for (int i = 1; i <= n; i++) {
int w;
cin >> w;
v[i].val = w, v[i].pre = i - 1, v[i].end = i + 1;
P[w] = i;
}
ll ans = 0;
//v[1].pre = n, v[n].end = 1;
for (int i = 1; i <= n; i++) {
int idx = P[i];
int num = 1;
int l = idx, r = idx;
while (num < m && v[l].pre != 0) {
l = v[l].pre;
num++;
}
while (num < m && v[r].end != n + 1)
{
r = v[r].end;
num++;
}
if (num >= m) {
while (r <= n && l <= idx) {
int L = l - v[l].pre, R = v[r].end - r;
ans += L * R * i;
l = v[l].end, r = v[r].end;
}
}
v[v[idx].pre].end = v[idx].end;
v[v[idx].end].pre = v[idx].pre;
}
cout << ans << endl;
}

signed main() {
IOS;
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

C. 子矩阵

  • 经典题,在单调栈修改的时候解决,对单调栈的理解很深刻
    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
    signed main()
    {
    IOS;
    int n, m;
    cin >> n >> m;
    vector<vector<char> > s(n + 1, vector<char>(m + 1));
    for (int i = 1; i <= n; i++)
    {
    for (int j = 1; j <= m; j++) {
    cin >> s[i][j];
    }
    }
    vector<vector<int>> DP(n + 2, vector<int>(m + 2, 0)), IDX(n + 2, vector<int>(m + 2, 0));
    int ans = 0;

    for (int i = 1; i <= n; i++)
    {
    //cout << s[i] << endl;
    for (int j = 1; j <= m; j++)
    {
    if (s[i][j] == 'R')
    continue;
    DP[i][j] = DP[i - 1][j] + 1;
    }
    }

    for (int i = 1; i <= n; i++)
    {
    deque<int> q;
    DP[i][0] = 0;
    q.push_back(0);
    for (int j = 1; j <= m; j++)
    {
    while (!q.empty() && DP[i][j] <= DP[i][q.back()])
    {
    //cout << j << ' ' << q.back() << ' ' << DP[i][j] << ' ' << IDX[i][j] << endl;
    ans = max(ans, (j - IDX[i][q.back()] - 1) * DP[i][q.back()]);
    q.pop_back();
    //cout << ans << ' ';
    }
    if (q.empty()) {
    q.push_back(0);
    }
    IDX[i][j] = q.back();
    int len = j - q.back();
    q.push_back(j);
    //cout << len << endl;
    ans = max(ans, DP[i][j] * len);
    }
    //cout << endl;
    }

    for (int i = 1; i <= n; i++)
    {
    deque<int> q;
    DP[i][m + 1] = 0;
    q.push_back(m + 1);
    for (int j = m; j >= 1; j--)
    {
    while (!q.empty() && DP[i][j] <= DP[i][q.back()])
    {
    //cout << j << ' ' << q.back() << ' ' << DP[i][j] << ' ' << DP[i][q.back()] << endl;
    q.pop_back();
    }
    if (q.empty()) {
    q.push_back(0);
    }
    int len = q.back() - j;
    q.push_back(j);
    //cout << len << endl;
    ans = max(ans, DP[i][j] * len);
    }
    //cout << endl;
    }

    cout << 3 * ans << endl;

    return 0;
    }

D. 环形最大子序和

  • 单调队列
    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
    const int N=1e5+10;

    int n,k,a[N<<1],sum[N<<1],l[N];

    int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)
    scanf("%d",&a[i]),a[i+n]=a[i];
    for(int i=1;i<=2*n;++i)
    sum[i]=sum[i-1]+a[i];

    deque<int>qu;
    qu.push_back(0);
    int ans=0;
    for(int i=1;i<=2*n;++i){
    while(!qu.empty()&&(i-qu.front()>k))qu.pop_front();
    ans=max(ans,sum[i]-sum[qu.front()]);
    l[i]=qu.front();
    while(!qu.empty()&&sum[qu.back()]>sum[i]) qu.pop_back();
    qu.push_back(i);
    }
    printf("%d ",ans);
    for(int i=1;i<=2*n;++i){
    if(sum[i]-sum[l[i]]==ans){
    printf("%d %d\n",l[i]+1==n?n:(l[i]+1)%n,i==n?n:i%n);
    break;
    }
    }
    return 0;
    }

E. 完美子区间

  • 很经典的二分查找
    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
    signed main() {
    IOS;
    int n, A, B;
    cin >> n >> A >> B;
    vector<int> v(n + 1);
    for (int i = 1; i <= n; i++)
    {
    cin >> v[i];
    }
    int l = 0, r = n;

    auto check = [&](int x) -> bool
    {
    deque<int> q1, q2;
    for (int i = 1; i <= n; i++)
    {
    while (!q1.empty() && q1.front() + x <= i)
    {
    q1.pop_front();
    }
    while (!q2.empty() && q2.front() + x <= i)
    {
    q2.pop_front();
    }
    while (!q1.empty() && v[q1.back()] < v[i])
    {
    q1.pop_back();
    }
    while (!q2.empty() && v[q2.back()] > v[i])
    {
    q2.pop_back();
    }
    q1.push_back(i);
    q2.push_back(i);
    if (i >= x)
    {
    int len = v[q1.front()] - v[q2.front()];
    if (A <= len && len <= B)
    {
    return true;
    }
    }
    }
    return false;
    };

    while (l + 1 < r) {
    int mid = (l + r) / 2;
    if (check(mid)) {
    l = mid;
    } else {
    r = mid;
    }
    }
    if (check(r)) {
    cout << r << endl;
    } else {
    cout << l << endl;
    }

    return 0;
    }

F. 最大数maxnumber

  • 很有意思的可以用ST表做的题
    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
    int query(int l,int r)
    {
    int len=lg[r-l+1];
    return max(f[r][len],f[l+pw[len]-1][len]);
    }
    signed main()
    {
    // std::ios_base::sync_with_stdio(false);
    // cin>>n>>mod;
    n=read();mod=read();
    for(int i=2;i<=n;++i)
    lg[i]=lg[i/2]+1;
    pw[0]=1;
    for(int i=1;i<=lg[n];++i)
    pw[i]=pw[i-1]*2;
    for(int i=1;i<=n;++i)
    {
    char opt=0;int x;
    // cin>>opt>>x;
    while(opt!='Q'&&opt!='A')opt=getchar();x=read();
    if(opt=='Q')
    {
    t=query(cnt-x+1,cnt);
    print(t);puts("");
    }
    else
    {
    x+=t;x%=mod;
    f[++cnt][0]=x;
    for(int j=1;j<=lg[cnt];++j)
    f[cnt][j]=max(f[cnt][j-1],f[cnt-pw[j-1]][j-1]);
    }
    }
    return 0;
    }

G. 跳石头

  • 一个很经典的用倍增解决问题的方法,在倍增的时候解决问题,要不会超时
    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
    #define int long long
    const int N = 1e6 + 5;
    int n, k, m, a[N], dp[N][2], ans[N];

    signed main() {
    scanf("%lld%lld%lld", &n, &k, &m);
    for (int i = 1; i <= n; i++) {
    scanf("%lld", &a[i]);
    ans[i] = i;
    }
    int l = 1;
    for (int i = 1; i <= n; i++) {
    while (l + k < n && a[l + k + 1] - a[i] < a[i] - a[l]) {
    l++;
    }
    dp[i][0] = (a[l + k] - a[i] <= a[i] - a[l] ? l : l + k);
    }
    for (int j = 0; j <= 60; j++) {
    if (m & (1ll << j)) {
    for (int i = 1; i <= n; i++) {
    ans[i] = dp[ans[i]][j & 1];
    }
    }
    for (int i = 1; i <= n; i++) {
    dp[i][(j + 1) & 1] = dp[dp[i][j & 1]][j & 1];
    }
    }
    for (int i = 1; i <= n; i++) {
    printf("%lld ", ans[i]);
    }
    }

H. 约瑟夫问题升级

  • 关键是做二分之前到提前预处理吧
    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
    #define int long long
    int a[100010];
    int n, m;
    int lowbit(int x) {
    return x & (-x);
    }
    void update(int pos, int num) {
    while(pos <= n) {
    a[pos] += num;
    pos += lowbit(pos);
    }
    }
    int sum(int pos) {
    int cnt = 0;
    while(pos) {
    cnt += a[pos];
    pos -= lowbit(pos);
    }
    return cnt;
    }
    signed main() {
    scanf("%lld%lld", &n, &m);
    for(int i = 1; i <= n; i++) {
    update(i, 1);
    }
    int last = 0;
    for(int i = 1; i <= n; i++) {
    int mm = m;
    int c = sum(n) - sum(last);
    int tot = sum(n) - sum(0);
    while(mm > tot) {
    mm -= tot;
    }
    if(c < mm) {
    mm -= c;
    last = 0;
    }
    //printf("%lld %lld %lld\n", last, c, mm);
    int l = last + 1, r = n;
    int ans;
    while(l <= r) {
    int mid = (l + r) / 2;
    int cc = sum(mid) - sum(last);
    if(cc >= mm) {
    r = mid - 1;
    ans = mid;
    }
    else {
    l = mid + 1;
    }
    //printf("%lld %lld\n", mid, cc);
    }
    last = ans;
    update(ans, -1);
    printf("%lld ", ans);
    //printf("\n");
    }
    }

I. 区间开根

  • 这个题的重点是开根最多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
    #define int long long
    constexpr int maxn=100005;
    //势能线段树.每个点最大被操作的次数是log级的.
    int n,tree[maxn<<2],m,avi[maxn<<2],ns[maxn];//tree[i]表示下标为i的节点的权值和,avi[i]表示这个节点是否全为1.
    void build(int l,int r,int p){
    if(l==r){tree[p]=ns[l]; return;}
    int mid=(l+r)>>1;
    build(l,mid,p*2);
    build(mid+1,r,p*2+1);
    tree[p]=tree[p*2]+tree[p*2+1];
    if(tree[p]<=r-l+1)avi[p]=1;
    }
    int query(int l,int r,int p,int nl,int nr){
    if(l>=nl and r<=nr)return tree[p];
    int mid=(l+r)>>1,ans=0;
    if(nl<=mid)ans+=query(l,mid,p*2,nl,min(nr,mid));
    if(nr>mid)ans+=query(mid+1,r,p*2+1,max(nl,mid+1),nr);
    return ans;
    }
    void update(int l,int r,int p,int nl,int nr){
    if(avi[p])return;
    if(l==r){
    tree[p]=(int)floor(sqrt(tree[p]));
    if(tree[p]<=1)avi[p]=1;
    return;
    }
    int mid=(l+r)>>1;
    if(nl<=mid and !avi[p*2])update(l,mid,p*2,nl,min(nr,mid));
    if(nr>mid and !avi[p*2+1])update(mid+1,r,p*2+1,max(nl,mid+1),nr);
    tree[p]=tree[p*2]+tree[p*2+1];
    avi[p]=avi[p*2] and avi[p*2+1];
    }
    int t,l,r;
    signed main(){
    cin >> n;
    for(int i=1;i<=n;i++)cin >> ns[i];
    cin >> m;
    build(1,n,1);
    for(int i=1;i<=m;i++){
    cin >> t >> s >> r;
    if(t==1){
    cout<<query(1,n,1,l,r)<<endl;
    }
    else if(t==2) update(1,n,1,l,r);
    }
    }