LOJ刷题记录:2006-2011(SCOI2015)

来源:互联网 发布:nginx 自建dns解析 编辑:程序博客网 时间:2024/06/07 13:32

LOJ刷题记录:2006-2011(SCOI2015)


loj#2006. 「SCOI2015」小凸玩矩阵

水题…

二分答案之后用二分图匹配判断能不能取出k个比当前答案小的。

#include <bits/stdc++.h>using namespace std;const int MAXN = 505;struct node {    int to, next, flow, neg;} edge[MAXN*MAXN];int head[MAXN], top = 0;inline void push(int i, int j, int f){    ++top, edge[top] = (node) {j, head[i], f, top+1}, head[i] = top;    ++top, edge[top] = (node) {i, head[j], 0, top-1}, head[j] = top;}const int S = MAXN-1, T = S-1;int lev[MAXN], vis[MAXN], bfstime = 0;int cur[MAXN];queue<int> que;bool bfs(){    vis[S] = ++bfstime, lev[S] = 0, que.push(S);    while (!que.empty()) {        int nd = que.front(); que.pop();        for (int i = head[nd]; i; i = edge[i].next) {            int to = edge[i].to;            if (vis[to] == bfstime || !edge[i].flow) continue;            vis[to] = bfstime, lev[to] = lev[nd]+1, que.push(to);        }    }    return vis[T] == bfstime;}int dfs(int nd, int flow){    if (nd == T || !flow) return flow;    int ans = 0, t;    for (int &i = cur[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (lev[to] != lev[nd]+1 || !edge[i].flow) continue;        t = dfs(to, min(flow, edge[i].flow));        ans += t, flow -= t, edge[i].flow -= t, edge[edge[i].neg].flow += t;        if (!flow) break;    }    return ans;}int dinic(){    int ans = 0;    while (bfs()) memcpy(cur, head, sizeof head), ans += dfs(S, INT_MAX);    return ans;}int n, m, k;int a[255][255];bool judge(int mid){    memset(head, 0, sizeof head);    top = 0;    for (int i = 1; i <= n; i++) push(S, i, 1);    for (int i = 1; i <= m; i++) push(i+n, T, 1);    for (int i = 1; i <= n; i++)        for (int j = 1; j <= m; j++)            if (a[i][j] <= mid)                push(i, n+j, 1);    return dinic() >= k;}int main(){    scanf("%d%d%d", &n, &m, &k);    k = n-k+1;    for (int i = 1; i <= n; i++)        for (int j = 1; j <= m; j++)            scanf("%d", &a[i][j]);    int l = 0, r = 1e9, mid;    while (l <= r) {        mid = (l+r)>>1;        if (judge(mid)) r = mid-1;        else l = mid+1;    }    printf("%d\n", r+1);    return 0;}

loj#2007. 「SCOI2015」国旗计划

细节神tm多……

考虑贪心,对于每一个战士,我们显然要选取能接上他火炬且右端点尽可能靠后点..这样我们就可以连一条有向边,然后每个战士就要找他的后继中第一个使得覆盖总长超过m的…这显然可以用倍增维护。

这样只要维护last[nd][i]为包含nd在内向后2i个点,nxt[nd][i]表示不包含nd向后2i个点,sum[nd][i]表示包含nd向后2i的覆盖总长。之所以要记录last,nxt两个东西就是要去除中间重复的部分。

#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;struct segment {    int l, r, id;    friend bool operator < (const segment &a, const segment &b)    { return a.l < b.l; }} p[MAXN*2];int n, m;int nxt[MAXN][20], last[MAXN][20];long long sum[MAXN][20];int dat[MAXN];int main(){    scanf("%d%d", &n, &m);    for (int i = 1; i <= n; i++) {        scanf("%d%d", &p[i].l, &p[i].r), p[i].id = i;        if (p[i].l > p[i].r) p[i].r += m;    }    sort(p+1, p+n+1);    for (int i = 1; i <= n; i++) p[i+n] = (segment){p[i].l+m, p[i].r+m, i};    int pt = 1;    for (int i = 1; i <= n; i++) {        while (p[pt+1].l <= p[i].r) pt++;        nxt[i][0] = pt<=n?pt:pt-n;        sum[i][0] = p[i].r-p[i].l+1;        last[i][0] = i;    }    for (int j = 1; j < 20; j++)        for (register int i = 1; i <= n; i++)            nxt[i][j] = nxt[nxt[i][j-1]][j-1], last[i][j] = nxt[last[i][j-1]][j-1];    for (int j = 1; j < 20; j++)        for (register int i = 1; i <= n; i++) {            int R1 = p[last[i][j-1]].r;            int R2 = p[nxt[i][j-1]].l; if (R2 > m) R2 -= m;            if (R1 > m) R1 -= m;            if (R1 < R2) R1 += m;            sum[i][j] = sum[i][j-1]+sum[nxt[i][j-1]][j-1]-(R1-R2+1);        }    for (int i = 1; i <= n; i++) {        long long cur = 0;        int pt = i, ans = 0;        int last_pos = p[i].l-1;        for (int j = 19; j >= 0; j--) {            int pre = pt;                if (sum[pt][j]+cur-(last_pos-p[pt].l+1) <= m) {                cur += sum[pt][j]-(last_pos-p[pt].l+1), last_pos = p[last[pt][j]].r, pt = nxt[pt][j], ans |= 1<<j;                if (last_pos > m) last_pos -= m;            }        }        dat[p[i].id] = ans+1;    }    for (int i = 1; i <= n; i++) {        printf("%d ", dat[i]);    }    puts("");    return 0;}

loj#2008. 「SCOI2015」小凸想跑步

半平面交还是非常好建的…算是第一个成功的板子吧..

#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;const double eps = 1e-8;struct point {    double x, y;    double ang;    friend point operator + (const point &a, const point &b)    { return (point){a.x+b.x, a.y+b.y}; }    friend point operator - (const point &a, const point &b)    { return (point){a.x-b.x, a.y-b.y}; }    friend double operator * (const point &a, const point &b)    { return a.x*b.y-a.y*b.x; }    friend point operator * (double lambda, const point &a)    { return (point){lambda*a.x, lambda*a.y};  }    void display()    { printf("(%.2f, %.2f) ", x, y); }};typedef point vec;struct half_plane {    point pt;    vec v;    friend bool operator < (const half_plane &a, const half_plane &b)    { return a.v.ang < b.v.ang; }    inline bool inside(point &p)    { return v*(p-pt) >= eps; }    friend point operator * (const half_plane &a, const half_plane &b)    {        double lambda = (a.pt*b.v-b.pt*b.v)/(b.v*a.v);        return a.pt+lambda*a.v;    }    void display()    { pt.display(); printf("+ k"); v.display(), printf("ang = %.2f\n", v.ang);}};half_plane p[MAXN*2];point q[MAXN*2];int l, r;double HPC(vector<half_plane> &v){    sort(v.begin(), v.end());    l = 1, r = 0;    for (auto i : v) {        while (l < r && !i.inside(q[r])) r--;        while (l < r && !i.inside(q[l+1])) l++;            if (l > r || abs(i.v.ang-p[r].v.ang) >= eps) p[++r] = i, q[r] = i*p[r-1];    }    while (l < r && !p[l].inside(q[r])) r--;    q[r+1] = p[r]*p[l];    q[r+2] = q[l+1];    double ans = 0;    if (r-l+1 >= 3) for (int i = l+1; i <= r+1; i++) ans += q[i]*q[i+1];    return ans/2;}int n;point pts[MAXN];vector<half_plane> v;int main(){    scanf("%d", &n);    for (int i = 0; i < n; i++) scanf("%lf%lf", &pts[i].x, &pts[i].y);    double sig = 0;    for (int i = 0; i < n; i++) sig += pts[i]*pts[(i+1)%n];    sig /= 2;    for (int i = 0; i < n; i++) {        vec ps = pts[(i+1)%n]-pts[i]; ps.ang = atan2(ps.y, ps.x);        v.push_back((half_plane){pts[i], ps});    }    for (int i = 1; i < n; i++) {        point p1 = pts[i], p2 = pts[(i+1)%n], pt;        double a = p1.y-p2.y-pts[0].y+pts[1].y, b = p2.x-p1.x-pts[1].x+pts[0].x;        double c = p1*p2-pts[0]*pts[1];        if (abs(b) <= eps) {            double xx = -c/a;            pt = (point) {xx, 0};         } else {            double xx = -c/b;            pt = (point) {0, xx};        }        v.push_back((half_plane){pt, (vec){b, -a, atan2(-a, b)}});    }    double ans = HPC(v);    printf("%.4f\n", ans/sig);    return 0;}

loj#2009. 「SCOI2015」小凸玩密室

好神啊…

用ui,j表示走完i的子树后走到i的深度为j的祖先的兄弟的最小代价;
用vi,j表示走完i的子树后走到i的深度为j的祖先的最小代价,用u算出v。
枚举起点,计算答案。

#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;int n;long long a[MAXN], b[MAXN];inline int lc(int nd){ return nd<<1; }inline int rc(int nd){ return nd<<1|1; }inline int fa(int nd){ return nd>>1; }inline long long L(int nd){ return b[(nd<<1)-1]; }inline long long R(int nd){ return b[nd<<1]; }long long f[MAXN][20], g[MAXN][20];int depth[MAXN];long long dis[MAXN];int main(){    scanf("%d", &n);    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);    for (int i = 1; i < n; i++) scanf("%lld", &b[i]);        depth[1] = 1, dis[1] = 0;    for (int i = 2; i <= n; i++) depth[i] = depth[fa(i)]+1, dis[i] = dis[fa(i)]+b[i-1];    for (int i = n; i >= 1; i--)        for (int j = depth[i], z = i; j > 1; j--, z = fa(z)) {            int bro = (z&1)?z-1:z+1;            if (lc(i)>n && rc(i)>n) f[i][j] = a[bro]*(dis[i]+dis[bro]-2*dis[fa(z)]);            else if (rc(i)>n) f[i][j] = a[lc(i)]*L(i)+f[lc(i)][j];            else f[i][j] = min(a[lc(i)]*L(i)+f[lc(i)][depth[i]+1]+f[rc(i)][j], a[rc(i)]*R(i)+f[rc(i)][depth[i]+1]+f[lc(i)][j]);        }    for (int i = n; i >= 1; i--)        for (int j = depth[i], z = i; j >= 0; j--, z = fa(z)) {            if (lc(i)>n && rc(i)>n) g[i][j] = (dis[i]-dis[z])*a[z];            else if (rc(i)>n) g[i][j] = a[lc(i)]*L(i)+g[lc(i)][j];            else g[i][j] = min(a[lc(i)]*L(i)+f[lc(i)][depth[i]+1]+g[rc(i)][j], a[rc(i)]*R(i)+f[rc(i)][depth[i]+1]+g[lc(i)][j]);        }    long long ans = g[1][0];    //cerr << ans << endl;    for (int i = 2; i <= n; i++) {        long long cnt = g[i][depth[i]-1];        int nd = i, p = fa(nd), bro = (nd&1)?nd-1:nd+1;        while (p) {            if (rc(p)<=n) cnt += b[bro-1]*a[bro]+g[bro][depth[p]-1];            else cnt += b[p-1]*a[fa(p)];            nd = p, p = fa(nd), bro = (nd&1)?nd-1:nd+1;        }        //cerr << cnt << endl;        ans = min(ans, cnt);    }    printf("%lld\n", ans);    return 0;}

loj2010有点毒…先空着

loj#2011. 「SCOI2015」情报传递

离线处理,树上可持久化线段树即可…然而并跑不过两个log的树剖…

#include <bits/stdc++.h>using namespace std;const int MAXN = 200005;int l[MAXN*20], r[MAXN*20], lc[MAXN*20], rc[MAXN*20], sum[MAXN*20], tp = 0, root[MAXN];void build(int &nd, int opl, int opr){    nd = ++tp, l[nd] = opl, r[nd] = opr;    if (opl < opr) build(lc[nd], opl, (opl+opr)>>1), build(rc[nd], ((opl+opr)>>1)+1, opr);}void modify(int pre, int &nd, int pos){    nd = ++tp, l[nd] = l[pre], r[nd] = r[pre], sum[nd] = sum[pre]+1;    if (l[nd] < r[nd]) {        int mid = (l[nd]+r[nd])>>1;        if (pos <= mid) rc[nd] = rc[pre], modify(lc[pre], lc[nd], pos);        else lc[nd] = lc[pre], modify(rc[pre], rc[nd], pos);    }}int query(int nd, int opl, int opr){    if (opl > opr) return 0;    if (l[nd] == opl && r[nd] == opr) return sum[nd];    else {        int mid = (l[nd]+r[nd])>>1;        if (opr <= mid) return query(lc[nd], opl, opr);        else if (opl > mid) return query(rc[nd], opl, opr);        else return query(lc[nd], opl, mid)+query(rc[nd], mid+1, opr);    }}struct node {    int to, next;} edge[MAXN];int head[MAXN], top = 0;inline void push(int i, int j){ edge[++top] = (node) {j, head[i]}, head[i] = top; }int n, f[MAXN], rt, q;int a[MAXN], b[MAXN], c[MAXN], d[MAXN];int T[MAXN], ans[MAXN];int fa[MAXN], vis[MAXN], depth[MAXN];int findf(int nd){ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }vector<pair<int, int> > qy[MAXN];void tarjan(int nd){    vis[nd] = 1, depth[nd] = depth[f[nd]]+1;    for (vector<pair<int, int> >::iterator i = qy[nd].begin(); i != qy[nd].end(); ++i)         if (vis[i->first]) ans[i->second] = findf(i->first);        for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to != f[nd]) tarjan(to);    }    fa[findf(nd)] = findf(f[nd]);}void dfs_build(int nd){    if (T[nd]) modify(root[f[nd]], root[nd], T[nd]);    else root[nd] = root[f[nd]];    for (int i = head[nd]; i; i = edge[i].next) {        int to = edge[i].to;        if (to != f[nd]) dfs_build(to);    }}int main(){    scanf("%d", &n);    for (int i = 1; i <= n; i++) {        scanf("%d", &f[i]);        if (f[i]) push(f[i], i);        else rt = i;    }    scanf("%d", &q);    for (int i = 1; i <= q; i++) {        scanf("%d", &a[i]);        if (a[i] == 1) scanf("%d%d%d", &b[i], &c[i], &d[i]), qy[b[i]].push_back(make_pair(c[i], i)), qy[c[i]].push_back(make_pair(b[i], i));        else scanf("%d", &b[i]), T[b[i]] = i;    }    tarjan(rt);    build(root[0], 1, n);    dfs_build(rt);    for (int i = 1; i <= q; i++) {        if (a[i] == 2) continue;        int lca = ans[i], min_pos = max(1, i-d[i]);        int sig = query(root[b[i]], 1, min_pos-1)+query(root[c[i]], 1, min_pos-1)-query(root[lca], 1, min_pos-1)-query(root[f[lca]], 1, min_pos-1);        printf("%d %d\n", depth[b[i]]+depth[c[i]]-depth[lca]*2+1, sig);    }    return 0;}