2018 HDU多校第三场赛后补题

2018 HDU多校第三场赛后补题

从易到难来写吧,其中题意有些直接摘了Claris的,数据范围是就不标了。
如果需要可以去hdu题库里找。题号是6319 ~ 6331。

L. Visual Cube

题意:

在画布上画一个三维立方体。

题解:

模拟即可。

代码:

#include <bits/stdc++.h>
using namespace std;

int a, b, c, R, C;
char g[505][505];
int main () {
    int T; cin >> T;
    for ( ; T; --T) {
        cin >> a >> b >> c;
        R = c * 2 + b * 2 + 1;
        C = a * 2 + b * 2 + 1;
        memset(g, '.', sizeof g);
        for (int i = R; i >= R - c * 2; i -= 2) {
            for (int j = 1; j <= a; ++j) {
                g[i][j * 2 - 1] = '+';
                g[i][j * 2] = '-';
            }
            g[i][a * 2 + 1] = '+';
            if (i == R - c * 2) continue;
            for (int j = 1; j <= a; ++j) {
                g[i - 1][j * 2 - 1] = '|';
            }
            g[i - 1][a * 2 + 1] = '|';
        }
        int ed = R, st = R - c * 2;
        for (int j = a * 2 + 1; j <= C; ++j) {
            int x = j - (a * 2);
            if (x & 1) {
                for (int i = st; i <= ed; ++i) {
                    int y = ed - i;
                    if (y & 1) g[i][j] = '|';
                    else g[i][j] = '+';
                }
            } else {
                for (int i = st; i <= ed; ++i) {
                    int y = ed - i;
                    if (y & 1) g[i][j] = '.';
                    else g[i][j] = '/';
                }
            }
            --ed, --st;
        }

        ed = b * 2 + 1, st = 1;
        for (int j = 1; j <= a * 2; ++j) {
            int x = j;
            if (x & 1) {
                for (int i = ed; i >= st; --i) {
                    int y = i - st;
                    if (y & 1) g[i][j + ed - i] = '/';
                    else g[i][j + ed - i] = '+';
                }
            } else {
                for (int i = ed; i >= st; --i) {
                    int y = i - st;
                    if (y & 1) ;
                    else g[i][j + ed - i] = '-';
                }
            }
        }

        for (int i = 1; i <= R; ++i) {
            for (int j = 1; j <= C; ++j) printf("%c", g[i][j]);
            puts("");
        }
    }
    return 0;
}

D. Euler Function

题意:

求第\(n\)个使得\(\varphi(k)\)为合数的\(k\)。

题解:

其实应该会有尝试,就是\(n\)到一定大小\(\varphi(k)\)都是大于\(2\)的偶数,同时也是合数。其实找一下规律可以发现只有\(k = 1, 2, 3, 6\)时\(\varphi(k)\)为质数。证明在Claris的题解里有写。

代码:

#include<bits/stdc++.h>
using namespace std;
int main () {
    int T,n;
    scanf("%d",&T);
    while (--T) {
        scanf("%d",&n);
        if (n==1) printf("5\n");
        else printf("%d\n",5+n);
    }
    return 0;
}

F. Grab The Tree

题意:

给定一棵\(n\)个点的树,每个点有权值。两个人玩游戏,先手需要占领若干不相邻的点,然后后手占领剩下所有点。每个人的得分为占领的点权异或和,分高的获胜。问最优策略下游戏的结果。

题解:

设\(sum\)为所有点权的异或和,\(A\)为先手得分,\(B\)为后手得分。
若\(sum=0\),则 \(A=B\),怎样都是平局。
否则考虑先手可以将原图分成两块,每一块里面的点都不相邻,然后取异或和较大的那块就好了,所以此时先手必胜。

代码:

#include<bits/stdc++.h>
#define N 100010
using namespace std;
int main () {
    int T,n,u,v,w;
    scanf("%d",&T);
    while (--T) {
        int sum=0;
        scanf("%d",&n);
        for(int i=1; i<=n; ++i) scanf("%d",&w),sum^=w;
        for(int i=1; i<n; ++i) scanf("%d%d",&u,&v);
        if (sum==0) printf("D\n"); else printf("Q\n");
    }
    return 0;
}

C. Dynamic Graph Matching

题意:

给定一个n个点的无向图,\(m\)次加边或者删边操作。
在每次操作后统计有多少个匹配包含\(k = 1, 2, ...,\frac n 2\)条边。
其中匹配是指不相交的边集。

题解:

设 f[i][S] 表示前i次操作,匹配点集为S的方案数。
设当前边的两个端点为\(x,y\)。
对于加边,显然有\(f[i][S]=f[i-1][S]+f[i-1][S-x-y]。\)
其实,也可以直接省略第一维,执行\(f[S]+=f[S-x-y]\)。正确性是显然的。
对于删边,由于前面加边的顺序无所谓,所以我们不妨设我们删除的正是上一次加入的边(为了方便理解)。
这样,我们执行的就是一个你操作罢了:\(f[S]-=f[S-x-y]\)。

代码:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;

const int mo = 1e9 + 7, N = 11;
int n, m, bc[1 << N], f[1 << N], ans[N]; char op[5];

int bitcnt (int x) {
    return x ? bitcnt(x >> 1) + (x & 1) : 0;
}
int main () {
    int T; cin >> T;
    for (int i = 0; i < (1 << N); ++i) bc[i] = bitcnt(i);
    for ( ; T; --T) {
        cin >> n >> m, f[0] = 1;
        for (int s = 1; s < (1 << n); ++s) f[s] = 0;
        for (int i = 1, x, y; i <= m; ++i) {
            scanf("%s%d%d", op, &x, &y), --x, --y;
            if (op[0] == '+') {
                for (int s = 0, t; s < (1 << n); ++s) {
                    if ((s >> x & 1) || (s >> y & 1)) continue;
                    t = s | 1 << x | 1 << y;
                    f[t] += f[s];
                    if (f[t] >= mo) f[t] -= mo;
                }
            } else {
                for (int s = (1 << n) - 1, t; ~s; --s) {
                    if ((s >> x & 1) || (s >> y & 1)) continue;
                    t = s | 1 << x | 1 << y;
                    f[t] -= f[s];
                    if (f[t] < 0) f[t] += mo;
                }
            }
            for (int i = 1; i <= (n >> 1); ++i) ans[i] = 0;
            for (int s = 1, t; s < (1 << n); ++s) {
                t = bc[s]; if (t & 1) continue; else t >>= 1;
                ans[t] += f[s];
                if (ans[t] >= mo) ans[t] -= mo;
            }
            for (int i = 1; i <= (n >> 1); ++i) {
                printf("%d%c", ans[i], i < (n >> 1) ? ' ' : '\n');
            }
        }
    }
    return 0;
}

G. Interstellar Travel

题意:

给定平面上\(n\)个点,起点横坐标最小,终点横坐标最大,所有点都在第一象限内。(包括坐标轴)
每次可以飞到一个横坐标严格更大的点,代价为两个坐标的叉积。
求起点到终点总代价最小的飞行路线,并输出字典序最小的路线。

题解:

因为叉积的几何意义就是有向平行四边形的面积的,然后根据题目给出的性质,很快就能反应过来,只需要维护一个凸包就好了(因为这个凸包面积最大,并且是负的,对应最小代价)。
注意会有重点的问题。

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 200005;

int n, top;

struct point {
    int x, y, i;
    point () {}
    point (int _x,int _y,int _i = 0) :
        x(_x), y(_y), i(_i) {}
} a[N], s[N];
point operator - (point u, point v) {
    return point(u.x - v.x, u.y - v.y, u.i);
}
bool cmp (point u, point v) {
    ll t = 1ll * u.x * v.y - 1ll * u.y * v.x;
    return t == 0 ? (u.x == v.x ? u.i < v.i: u.x < v.x) : t < 0;
}

bool cmp2 (point u, point v) {
    ll t = 1ll * u.x * v.y - 1ll * u.y * v.x;
    return t == 0 ? u.i < v.i : t < 0;
}
void solve () {
    s[top = 1] = a[1];
    for (int i = 2; i <= n; ++i) {
        if (a[i].x == a[i - 1].x && a[i].y == a[i - 1].y) continue;
        while (top >= 2 && cmp2(a[i] - s[top - 1], s[top] - s[top - 1])) --top;
        s[++top] = a[i];
    }
}

int main () {
    int T; cin >> T;
    for ( ; T; --T) {
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            scanf("%d%d", &a[i].x, &a[i].y);
            a[i].i = i;
        }
        sort(a + 2, a + n, cmp);
        solve();
        for (int i = 1; i <= top; ++i) {
            printf("%d%c", s[i].i, i < top ? ' ' : '\n');
        }
    }
    return 0;
}

A. Ascending Rating

题意:

给定一个序列\(a[1..n]\),对于每个长度为\(m\)的连续子区间,求出区间\(a\)的最大值以及从左往右扫描该区间时\(a\)的最大值的变化次数。

题解:

这题反正来似乎比较容易。。
考虑导致维护一个单调递减的单调队列,其中队列大小\(size\)就是当前区间最大值的变化次数,队列中最后一个元素就是区间最大值。具体想一想就好了。其实我是不会打单调数据结构的

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 10000005;
int n, m, k, p, q, r, mo, a[N];
int h, t, Q[N];
ll A, B;

int read () {
    int x = 0; char ch = getchar();
    for ( ; ch < '0' || ch > '9'; ch = getchar());
    for ( ; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x;
}
int main () {
    int T; cin >> T;
    for ( ; T; --T) {
        cin >> n >> m >> k >> p >> q >> r >> mo;
        for (int i = 1; i <= k; ++i) a[i] = read();
        for (int i = k + 1; i <= n; ++i) a[i] = (1ll * p * a[i - 1] + 1ll * q * i + r) % mo;
        h = 0, t = 1, A = B = 0;
        for (int i = n; i >= n - m + 1; --i) {
            if (a[i] < a[Q[h]]) Q[++h] = i;
            else {
                for ( ; t <= h && a[i] >= a[Q[h]]; ) --h;
                Q[++h] = i;
            }
        }
        A += a[Q[t]] ^ (n - m + 1);
        B += (h - t + 1) ^ (n - m + 1);
        for (int i = n - m; i; --i) {
            if (Q[t] == i + m) ++t;
            if (a[i] < a[Q[h]]) Q[++h] = i;
            else {
                for ( ; t <= h && a[i] >= a[Q[h]]; ) --h;
                Q[++h] = i;
            }
            A += a[Q[t]] ^ i;
            B += (h - t + 1) ^ i;
        }
        cout << A << " " << B << endl;
    }
    return 0;
}

I. Random Sequence

题意:

给定一个正整数序列\(a[1..n]\)和一个\(v\)数组,每个数在\([1, m]\)之间,有些数已知,有些数未知。
求未知数随机填的情况下以下值的期望:
\[ \begin{eqnarray*} \prod_{i=1}^{n-3} v[\gcd(a_i,a_{i+1},a_{i+2},a_{i+3})] \end{eqnarray*} \]

题解:

观察下这题的数据范围,发现可以写个dp。
\(f[i][x][y][z]\)表示将位置\(1~i\)(可能进行的)填数后,\(a[i]=x,gcd(a[i],a[i-1])=y,gcd(a[i],a[i-1],a[i-2])=z\)的期望答案(即套上连乘符号的)。
则我们可以很轻松知道转移方程。
但要注意的是这个连乘符号可能导致算期望的式子和我们平时做到的略有不同。总之,是有这样的式子:
\[ E[s] = V[s] * \begin{eqnarray*} \sum_{pre(s)}^{} E[pre]*pro[pre,s] \end{eqnarray*} \]
但别以为这样就完事了。
注意到这样会MLE,所以你可以选择开滚动数组。
然后你又被卡TLE了。注意到对于一个合法状态\((x,y,z)\),满足\(z|y\)且\(y|x\)。利用这个就可以简化状态数。
然后你就稳了。注意前三项是不计贡献的。

代码:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int N = 105, mo = 1e9 + 7;
int n, m, g[N][N], inv[N], a[N], v[N];
int f[2][N][N][N];

int ksm (int b, int p) {
    if (p < 2) return p ? b : 1;
    int t = ksm(b, p >> 1); t = 1ll * t * t % mo;
    return p & 1 ? 1ll * t * b % mo : t;
}
int main () {
    int T; cin >> T;
    for (int i = 0; i <= 100; ++i) g[0][i] = g[i][0] = i;
    for (int i = 1; i <= 100; ++i)
    for (int j = 1; j <= 100; ++j) g[i][j] = __gcd(i, j);
    for (int i = 1; i <= 100; ++i) inv[i] = ksm(i, mo - 2);
    for ( ; T; --T) {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        for (int i = 1; i <= m; ++i) scanf("%d", &v[i]);
        memset(f, 0, sizeof f);
        int nx, ny, nz, tmp;
        for (int z = 1; z <= m; ++z)
        for (int y = 1; y <= m; ++y)
        for (int x = 1; x <= m; ++x) {
            if (a[1] && a[1] != z) continue;
            if (a[2] && a[2] != y) continue;
            if (a[3] && a[3] != x) continue;
            nx = x, ny = g[x][y], nz = g[ny][z];
            tmp = 1;
            if (!a[1]) tmp = 1ll * tmp * inv[m] % mo;
            if (!a[2]) tmp = 1ll * tmp * inv[m] % mo;
            if (!a[3]) tmp = 1ll * tmp * inv[m] % mo;
            f[1][nx][ny][nz] += tmp;
            if (f[1][nx][ny][nz] >= mo) f[1][nx][ny][nz] -= mo;
        }
        for (int i = 3, c; i <= n; ++i) {
            c = i & 1, memset(f[c ^ 1], 0, sizeof f[c ^ 1]);
            for (int z = 1; z <= m; ++z)
            for (int y = z; y <= m; y += z)
            for (int x = y; x <= m; x += y) if (f[c][x][y][z]) {
                if (a[i + 1]) {
                    nx = a[i + 1], ny = g[x][a[i + 1]], nz = g[y][a[i + 1]];
                    f[c ^ 1][nx][ny][nz] += 1ll * f[c][x][y][z] * v[g[a[i + 1]][z]] % mo;
                    if (f[c ^ 1][nx][ny][nz] >= mo) f[c ^ 1][nx][ny][nz] -= mo;
                    continue;
                }
                for (int j = 1; j <= m; ++j) {
                    nx = j, ny = g[x][j], nz = g[y][j];
                    f[c ^ 1][nx][ny][nz] += 1ll * f[c][x][y][z] * v[g[j][z]] % mo * inv[m] % mo;
                    if (f[c ^ 1][nx][ny][nz] >= mo) f[c ^ 1][nx][ny][nz] -= mo;
                }
            }
        }
        int ans = 0;
        for (int z = 1; z <= m; ++z)
        for (int y = z; y <= m; y += z)
        for (int x = y; x <= m; x += y) ans = (ans + f[n & 1][x][y][z]) % mo;
        cout << ans << endl;
    }
    return 0;
}

M. Walking Plan

题意:

给定一个\(n\)个点,\(m\)条边的有向图,\(q\)次询问\(s\)到\(t\)经过至少\(k\)条边的最短路。

题解:

注意观察数据范围。
显然可以预处理出\(f[k][i][j]\)表示i经过恰好k条边到达j的最短路。
转移方程:\(f[k][i][j]=min(f[k?1][i][x]+w[x][j])\)。(看f[k]长得很像个矩阵,不过这题并不用这个性质)
当然你可以选择用\(O(n^3 * 10000)\)的时间预处理,然后愉快地gg了。
当然还有更好的选择。我们来分块!
我们预处理出\(f[k][i][j]\)表示i经过恰好k条边到达j的最短路,其中\(0<=k<=100\)。
再预处理出\(g[k][i][j]\)表示i经过恰好100k条边到达j的最短路,其中\(0<=k<=100\)。
对于一个询问,设\(A = k / 100\),\(B\) \(=\) \(k\) \(mod\) \(100\)。
则我们枚举中间点,\(ans=min(a[A][s][x]+b[B][x][t])\)。
这样是不是就好了?
错!
题目中要求的是至少经过\(k\)条边,而非恰好。
所以我们在\(b\)数组中做点手脚。其实就是先跑出原图的最短路,然后以“恰当的方式”更新\(b\)数组就好了。
这个恰当的方式可以参考一下具体实现。
复杂度是\(O(n^3 \sqrt k+ nq)\)。

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 55, K = 10005, S = 105, inf = 0x3f3f3f3f;
int n, m, q, ans, g[N][N], f[N][N], A[S][N][N], B[S][N][N];

int main () {
    int T; cin >> T;
    for ( ; T; --T) {
        cin >> n >> m;
        memset(g, 0x3f, sizeof g);
        memset(A, 0x3f, sizeof A);
        memset(B, 0x3f, sizeof B);
        for (int i = 1, x, y, z; i <= m; ++i) {
            scanf("%d%d%d", &x, &y, &z);
            g[x][y] = min(g[x][y], z);
            B[1][x][y] = g[x][y];
        }
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j) A[0][i][j] = B[0][i][j] = i == j ? 0 : inf;
        for (int k = 2; k <= 100; ++k) {
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j)
                    for (int x = 1; x <= n; ++x)
                    B[k][i][j] = min(B[k][i][j], B[k - 1][i][x] + B[1][x][j]);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) A[1][i][j] = B[100][i][j];
        }
        for (int k = 2; k <= 100; ++k) {
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j)
                    for (int x = 1; x <= n; ++x)
                    A[k][i][j] = min(A[k][i][j], A[k - 1][i][x] + A[1][x][j]);
        }
        for (int i = 1; i <= n; i++) g[i][i] = 0;
        for (int x = 1; x <= n; ++x) {
            for (int i = 1; i <= n; ++i) {
                for (int j = 1; j <= n; ++j) g[i][j] = min(g[i][j], g[i][x] + g[x][j]);
            }
        }
        for (int k = 0; k <= 100; ++k) {
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j) f[i][j] = inf;
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j)
                    for (int x = 1; x <= n; ++x) f[i][j] = min(f[i][j], B[k][i][x] + g[x][j]);
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j) B[k][i][j] = f[i][j];
        }
        cin >> q;
        for (int s, t, k, u, v; q; --q) {
            scanf("%d%d%d", &s, &t, &k), ans = inf;
            u = k / 100, v = k % 100;
            for (int i = 1; i <= n; ++i) {
                ans = min(ans, A[u][s][i] + B[v][i][t]);
            }
            if (ans >= inf) ans = -1;
            printf("%d\n", ans);
        }
    }
    return 0;
}

H. Monster Hunter

题意:

给定一棵\(n\)个点的树,除\(1\)外每个点有一只怪兽,打败它需要先消耗\(a[i]\)点 HP,再恢复\(b[i]\)点 HP。
求从\(1\)号点出发按照最优策略打败所有怪兽一开始所需的最少HP(其中,过程中不能出现HP为负的情况)。

题解:

这题教会了我们从简单的情况思考。
假设没有“树”的限制,即没有定死的先后顺序。怎么办?
显然贪心。
将怪兽分成两类考虑:\(a<b\)和\(a>=b\)。其实这种贪心见到过不止一次
显然应该先打前一类,再打后一类。
前一类的显然是先打a小的。可以感性理解一下。其实很好证,时间关系,不证了。
后一类呢,稍微要想一想。我就拖一下Claris的证明吧:
对于\(a>=b\)的怪兽,考虑两只怪兽\(i\),\(j\)。
先打\(i\)再打\(j\): $HPmin=HP - a[i] + b[i] - a[j] $
先打\(j\)再打\(i\): $HPmin=HP - a[j] + b[j] - a[i] $
这样的话,我们必定会选择\(b\)大的先打。
再回头考虑原问题。设排好序后,顺序为\(p[1],p[2],...,p[n]\)
若\(p[1] = 1\),那么第一步打\(p[1]\)一定最优。
否则在打完\(p[1]\)的父亲\(fa\)后,紧接着打\(p[1]\)一定最优。直接合并它们。
剩下的是规模为\(n-1\)的子问题。
注意这里合并和快速搜索最优分别要用到并查集和堆。
复杂度是\(O(nlogn)\)的。

代码:

#include <bits/stdc++.h>
#define mp make_pair
#define ll long long
using namespace std;

const int N = 100005;

int n, P, a[N], b[N];
int tot, lnk[N], nxt[N << 1], son[N << 1], fa[N];
bool dead[N];

struct mons {
    ll a, b; int i, t;
    mons () {}
    mons (ll _a, ll _b, int _i = 0,int _t = 0) :
        a(_a), b(_b), i(_i), t(_t) {}
    bool operator < (const mons &o) const {
        bool k1 = a < b, k2 = o.a < o.b;
        if (k1 != k2) return k1 < k2;
        else return a >= b ? b < o.b : a > o.a;
    }
    void operator += (const mons &o) {
        ll na = max(a, a - b + o.a), nb = - a + b - o.a + o.b + na;
        a = na, b = nb;
    }
} m[N], cur;
priority_queue <mons> q;

int read () {
    int x = 0; char ch = getchar();
    for ( ; ch < '0' || ch > '9'; ch = getchar());
    for ( ; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x;
}
void add (int x, int y) {
    nxt[++tot] = lnk[x], lnk[x] = tot, son[tot] = y;
}
void dfs (int x, int p) {
    fa[x] = p;
    for (int j = lnk[x]; j; j = nxt[j]) {
        if (son[j] == p) continue;
        dfs(son[j], x);
    }
}
int get (int x) {
    return dead[fa[x]] ? fa[x] = get(fa[x]) : fa[x];
}
int main () {
    for (int T = read(); T; --T) {
        n = read(), tot = 0;
        for (int i = 1; i <= n; ++i) lnk[i] = 0, dead[i] = 0;
        for (int i = 1; i <= n * 2; ++i) nxt[i] = 0;
        for ( ; !q.empty(); q.pop());
        m[1] = mons(0, 0, 1, 0);
        for (int i = 2; i <= n; ++i) {
            m[i].a = read(), m[i].b = read();
            m[i].i = i, m[i].t = 0, q.push(m[i]);
        }
        for (int i = 1, x, y; i < n; ++i) {
            x = read(), y = read();
            add(x, y), add(y, x);
        }
        dfs(1, 0), dead[1] = 0;
        for (int sec = 1; !q.empty(); ++sec) {
            cur = q.top(), q.pop();
            if (cur.t != m[cur.i].t || dead[cur.i]) continue;
            get(cur.i), dead[cur.i] = 1;
            P = get(cur.i);
            m[P] += m[cur.i], m[P].t = sec;
            if (P > 1) q.push(m[P]);
        }
        cout << max(0ll, m[1].a) << endl;
    }
    return 0;
}

剩下的题可能有点难搞了。。

原文地址:https://www.cnblogs.com/whc200305/p/9399240.html

时间: 2024-10-13 05:11:25

2018 HDU多校第三场赛后补题的相关文章

hdu 5317 RGCDQ (2015多校第三场第2题)素数打表+前缀和相减求后缀(DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5317 题意:F(x) 表示x的不同质因子的个数结果是求L,R区间中最大的gcd( F(i) , F(j) ),i.j在L,R区间内. 思路:因为2<=L < R<=1000000,所以他们的质因子最多的个数也就7个,也就是说1<=F(x)<=7,因为要求最大的GCD,所以只要知道在L,R区间内每个F(x)的情况就可以知道结果. 代码: 1 #include <stdio.h

hdu 5316 Magician(2015多校第三场第1题)线段树单点更新+区间合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5316 题意:给你n个点,m个操作,每次操作有3个整数t,a,b,t表示操作类型,当t=1时讲a点的值改成b:当t=0时,查询区间a,b之间最大的子序列和,这个子序列中的相邻的元素的原来的下标奇偶性都不同. 思路:这道题难点就在查询,其余都是模板,而根据查询,你只要分别把下一个区间的奇偶最大的情况分别比较,合并到上一个区间这样可以构建一个每个节点存有区间中奇开头偶开头,奇结尾,偶结尾这些区间情况的树.

hdu多校第三场 1006 (hdu6608) Fansblog Miller-Rabin素性检测

题意: 给你一个1e9-1e14的质数P,让你找出这个质数的前一个质数Q,然后计算Q!mod P 题解: 1e9的数据范围pass掉一切素数筛法,考虑Miller-Rabin算法. 米勒拉宾算法是一种判断素数的随机化算法,由于其随机性,它不能保证总是正确的,但其对于一个素数,总会返回素数的结果,对于一个合数,才有极小概率返回素数的结果(假阳性). 米勒拉宾算法对于单个素数的判断时间复杂度为$O(log^3n)$.(因为1e14相乘会爆longlong,模乘要写成龟速乘,因此要多一个log) 18

hdu多校第三场 1007 (hdu6609) Find the answer 线段树

题意: 给定一组数,共n个,第i次把第i个数扔进来,要求你删掉前i-1个数中的一些(不许删掉刚加进来这个数),使得前i个数相加的和小于m.问你对于每个i,最少需要删掉几个数字. 题解: 肯定是优先删大数,一开始想的方法类似于尺取,就是维护一个大顶堆作为现有的数,小顶堆作为要删的数,每次大顶堆的元素总和大于m了,就把堆顶扔进小顶堆,扔完了,再把小顶堆的堆顶扔回大顶堆,在会导致大顶堆值总和大于m时停止. 但是很容易会被1 1 1 1 1 1 1 1 1 1 100 这样的数据卡掉. 然后想到了,维护

2014多校第三场1005 || HDU 4891 The Great Pan(模拟)

题目链接 题意 : 给你n行字符串,问你有多少种理解方式.有两大类的理解 (1){A|B|C|D|...}代表着理解方式可以是A,可以是B或C或者D. (2)$blah blah$,在$$这两个符号中间,如果是不连续的空格的那个位置就有2种理解方式,可以理解为没有空格也可以理解为有空格.如果有连续N个空格的位置,那里就有N+1种理解方式. 最后所有的理解方式相乘,数据保证$一定与$匹配,{一定与匹配},不会有任何嵌套,类似{$$}或者{{}}或者${}$这种情况都不会出现,也不会有{$}这种情况

2019湖南多校第三场

解题过程 开场lfw过A,然后byf突然想到E的构造方法,WA了一发开始查错,shl中途看G,说"这不是bzoj原题吗?"拿到一血带歪榜,然后byf该出E拿到一血又带歪榜...shl写出B,之后lfw C想复杂调不出,shl WA一发C过掉 ,byf发现F题是几何题,想起了寒假刚几何起步的lfw,byf开始带着lfw想防AK题.K题shl先贪心再二分做了很久最后过掉,K题lfw想网络流,然后失败了,最后byf想出费用流,好像可以,但是shl过掉了. 赛后发现G题真的是BZOJ原题,而且

2014 HDU多校弟五场J题 【矩阵乘积】

题意很简单,就是两个大矩阵相乘,然后求乘积. 用 Strassen算法 的话,当N的规模达到100左右就会StackOverFlow了 况且输入的数据范围可达到800,如果变量还不用全局变量的话连内存开辟都开不出来 1 #pragma comment(linker, "/STACK:16777216") 2 #include <iostream> 3 #include <stdio.h> 4 #define ll long long 5 using namesp

2014 HDU多校弟八场H题 【找规律把】

看了解题报告,发现看不懂 QAQ 比较简单的解释是这样的: 可以先暴力下达标,然后会发现当前数 和 上一个数 的差值是一个 固定值, 而且等于当前数与i(第i个数)的商, 于是没有规律的部分暴力解决,有规律的套公式 //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include <stdio.h> #include <iostream> #include <cstring&g

2014 HDU多校弟九场I题 不会DP也能水出来的简单DP题

听了ZWK大大的思路,就立马1A了 思路是这样的: 算最小GPA的时候,首先每个科目分配到69分(不足的话直接输出GPA 2),然后FOR循环下来使REMAIN POINT减少,每个科目的上限加到100即可 算最大GPA的时候,首先每个科目分配到60分,然后FOR循环下来使REMAIN POINT减少,每个科目的上限加到85即可,如果还有REMAIN POINT,就FOR循环下来加到100上限即可 不会DP 阿 QAQ 过段时间得好好看DP了  =  = 于是默默的把这题标记为<水题集> //