2019-08-23 纪中NOIP模拟A组

T1 [JZOJ2908] 矩阵乘法

题目描述

  给你一个 N*N 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 K 小数。

数据范围

  对于 $20\%$ 的数据,$N \leq 100$,$Q \leq 10^3$

  对于 $40\%$ 的数据,$N \leq 300$,$Q \leq 10^4$

  对于 $60\%$ 的数据,$N \leq 400$,$Q \leq 3 \times 10^4$

  对于 $100\%$ 的数据,$N \leq 500$,$Q \leq 6 \times 10^4$

分析

  作为一个数据结构菜鸡怎么可能会做这种题目

  //二维树状数组+整体二分

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define inf 0x3f3f3f3f
#define N 502
#define M 60005

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while ( isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    return x *= f;
}

int n, m, t[N][N];
int id[M], cur[M], ans[M], q1[M], q2[M];

struct Mat {int x, y, v;} g[N * N];
bool cmp(Mat a, Mat b) {return a.v < b.v;}
struct Query {int x1, y1, x2, y2, k;} q[M];

inline int lowbit(int x) {return x & -x;}

inline void add(int x, int y, int v) {
    for (int i = x; i <= n; i += lowbit(i))
        for (int j = y; j <= n; j += lowbit(j))
            t[i][j] += v;
}

inline int pre(int x, int y) {
    int sum = 0;
    for (int i = x; i; i -= lowbit(i))
        for (int j = y; j; j -= lowbit(j))
            sum += t[i][j];
    return sum;
}

inline int sub(int x1, int y1, int x2, int y2) {
    int sum = pre(x2, y2);
    sum -= pre(x1 - 1, y2) + pre(x2, y1 - 1);
    sum += pre(x1 - 1, y1 - 1);
    return sum;
}

void solve(int l, int r, int ql, int qr) {
    if (ql > qr) return;
    if (l == r) {
        for (int i = ql; i <= qr; i++) ans[id[i]] = g[l].v;
        return;
    }
    int mid = (l + r) >> 1, cnt1 = 0, cnt2 = 0;
    for (int i = l; i <= mid; i++) add(g[i].x, g[i].y, 1);
    for (int i = ql, j = id[i]; i <= qr; j = id[++i]) {
        int sum = cur[j] + sub(q[j].x1, q[j].y1, q[j].x2, q[j].y2);
        if (sum >= q[j].k) q1[++cnt1] = j;
        else q2[++cnt2] = j, cur[j] = sum;
    }
    int cnt = ql - 1;
    for (int i = 1; i <= cnt1; i++) id[++cnt] = q1[i];
    for (int i = 1; i <= cnt2; i++) id[++cnt] = q2[i];
    for (int i = l; i <= mid; i++) add(g[i].x, g[i].y, -1);
    solve(l, mid, ql, ql + cnt1 - 1);
    solve(mid + 1, r, ql + cnt1, qr);
}

int main(){
    n = read(); m = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            g[(i - 1) * n + j].x = i;
            g[(i - 1) * n + j].y = j;
            g[(i - 1) * n + j].v = read();
        }
    sort(g + 1, g + n * n + 1, cmp);
    for (int i = 1; i <= m; i++) {
        q[i].x1 = read(); q[i].y1 = read();
        q[i].x2 = read(); q[i].y2 = read();
        q[i].k = read(); id[i] = i;
    }
    solve(1, n * n, 1, m);
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    
    return 0;
}
View Code

T2 [JZOJ3410] Tree

题目描述

  Wayne 在玩儿一个很有趣的游戏。在游戏中,Wayne 建造了 N 个城市,现在他想在这些城市间修一些公路,当然并不是任意两个城市间都能修,为了道路系统的美观,一共只有 M 对城市间能修公路,即有若干三元组 (Ui, Vi,Ci) 表示 Ui 和 Vi 间有一条长度为 Ci 的双向道路。当然,游戏保证了,若所有道路都修建,那么任意两城市可以互相到达。

  Wayne 拥有恰好 N - 1 支修建队,每支队伍能且仅能修一条道路。当然,修建长度越大,修建的劳累度也越高,游戏设定是修建长度为 C 的公路就会有 C 的劳累度。当所有的队伍完工后,整个城市群必须连通,而这些修建队伍们会看看其他队伍的劳累情况,若劳累情况差异过大,可能就会引发骚动,不利于社会和谐发展。Wayne 对这个问题非常头疼,于是他想知道,这 N - 1 支队伍劳累度的标准差最小能有多少。

数据范围

  对于 $20\%$ 的数据,$M \leq 20$

  对于另外 $30\%$ 的数据,$C \leq 10$

  对于 $100\%$ 的数据,$N \leq 100$,$M \leq 2000$,$C \leq 100$

分析

  考场上写了个水法,后来发现似乎是正解喜闻乐见

  首先这是一道最小标准差生成树

  刚开始发现 $C$ 很小,感觉解法和这个有关,于是想到枚举平均数

  但是直接枚举出平均数是不存在的,考虑到标准差是个表示数据稳定性的量,所以可以在合理范围内枚举长度值,选取长度与之最接近的道路做生成树,然后算出真实平均值求标准差来更新答案

  由于所有道路的长度都是正整数,所以对于 $x \in (0.5k,0.5k+0.5) \; (k \in N)$,当 $k$ 取某固定值时,无论 $x$ 取何值,所有道路按长度与 $x$ 的差值从小到大排序的结果是相同的

  因此只需要每隔 $0.25$ 枚举一个长度值,当然也可以将所有道路长度都乘以 $4$

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 105
#define M 2005 

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x *= f;
}

double ans = 1000000000.0;
int n, m, maxe, mine = inf;
int f[N], q[N];

struct Edge {int u, v, w, d;} e[M];
bool cmp(Edge a, Edge b) {return a.d < b.d;}

int find(int x) {
    if (x == f[x]) return x;
    return f[x] = find(f[x]);
}

int main() {
    n = read(); m = read();
    for (int i = 1; i <= m; i++) {
        e[i].u = read(); e[i].v = read(); e[i].w = read() * 4;
        maxe = max(maxe, e[i].w); mine = min(mine, e[i].w);
    }
    for (int k = mine; k <= maxe; k++) {
        for (int i = 1; i <= m; i++) e[i].d = abs(e[i].w - k);
        sort(e + 1, e + m + 1, cmp);
        int cnt = 0, sum = 0; double now = 0;
        for (int i = 1; i <= n; i++) f[i] = i;
        for (int i = 1; i <= m; i++) {
            int x = find(e[i].u), y = find(e[i].v);
            if (x == y) continue; f[x] = y;
            q[++cnt] = i; sum += e[i].w / 4;
            if (cnt == n - 1) break;
        }
        double avg = (double)sum / (double)(n - 1);
        for (int i = 1; i < n; i++)
            now += (e[q[i]].w / 4.0 - avg) * (e[q[i]].w / 4.0 - avg);
        now = sqrt(now / (n - 1));
        ans = min(ans, now);
    }
    printf("%.4lf", ans);
    
    return 0;
}
View Code

T3 [JZOJ3682] Points and Segments

题目描述

  Lahub 在几何问题上准备得不充分,但是他听说这一年的 IOI 选拔夏令营中会出现许多几何问题。深陷恐惧的 Lahub 把他自己锁在了地下室里,并且开始思考这一类别的新题目。其中一个如下。
Lahub 想要在 OX 轴上画 n 条不同的线段 [li,ri]。线段可以是红色和蓝色其中任意一种。图画是“好”的当且仅当满足接下来的条件:

  对于每个 OX 轴上的点 x,考虑所有包含点 x 的线段,设有 rx 个红线段和 bx 个蓝线段包含点 x,必须满足不等式 |rx-bx|<=1。

  线段 [l,r] 包含点 x 当且仅当 l<= x<= r。

  Lahub 给你所有线段的左右端点,你不得不找到一个“好”的画法给他。

数据范围

  对于 $20\%$ 的数据,$N \leq 10$

  对于 $40\%$ 的数据,$N \leq 10^3$

  对于 $100\%$ 的数据,$N \leq 10^5$

分析

  根据差分的思想,一段区间被染色,就相当于 $l$ 加,$r+1$ 减

  首先当然要把点离散化,然后对于每个区间我们可以在 $l$ 和 $r+1$ 之间连一条无向边

  如果图中存在奇点(一定为偶数个),则可以从小到大组成若干对点,在每对点之间连一条虚边(任意两条虚边之间不存在重合部分),此时就形成了一个欧拉回路

  于是可以在图上跑一边欧拉回路,若经过某条边时方向向左,则对应区间被染成红色,反之则被染成蓝色,这样就能保证在每个点上红色和蓝色的覆盖次数之差的绝对值不超过 $1$

  因为这是个欧拉回路,覆盖每个点的红色区间和蓝色区间数量一定相等,且每个点最多被一条虚边覆盖,所以删去虚边后仍然符合题意

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define inf 0x3f3f3f3f
#define N 200005

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x *= f;
}

int n, m, tot = 1, last;
int to[N << 1], num[N << 1], nxt[N << 1], head[N];
int q[N], d[N], vis[N], used[N << 1], ans[N >> 1];
struct Data {int l, r;} t[N >> 1];

void add(int u, int v, int w) {
    to[++tot] = v; num[tot] = w;
    nxt[tot] = head[u]; head[u] = tot;
}

void dfs(int x) {
    vis[x] = 1;
    for (int &i = head[x]; i; i = nxt[i])
        if (!used[i]) {
            used[i] = used[i ^ 1] = 1;
            ans[num[i]] = (to[i] > x);
            dfs(to[i]);
        }
}

int main(){
    n = read();
    for (int i = 1; i <= n; i++) {
        t[i].l = read(); t[i].r = read() + 1;
        q[++m] = t[i].l; q[++m] = t[i].r;
    }
    sort(q + 1, q + m + 1);
    m = unique(q + 1, q + m + 1) - q - 1;
    for (int i = 1; i <= n; i++) {
        t[i].l = lower_bound(q + 1, q + m + 1, t[i].l) - q;
        t[i].r = lower_bound(q + 1, q + m + 1, t[i].r) - q;
        add(t[i].l, t[i].r, i); add(t[i].r, t[i].l, i);
        d[t[i].l]++; d[t[i].r]++;
    }
    for (int i = 1; i <= m; i++)
        if (d[i] & 1) {
            if (last) {
                add(last, i, 0); add(i, last, 0);
                d[last]++; d[i]++; last = 0;
            }
            else last = i;
        }
    for (int i = 1; i <= m; i++) if (!vis[i]) dfs(i);
    for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
    
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Pedesis/p/11402597.html