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

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值