2019.9.24模拟赛

T1 周

该题目过水已隐藏
$ O(2^{15}) $ 暴力dfs就好了。

直接扔代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
const int MAXN = 20;
int a[MAXN], b[MAXN], c[MAXN], d[MAXN];
int ans = 0;
void dfs(int day, int oi, int whk) {
    if (day > n) {
        ans = max(ans, oi * whk);
        return;
    }
    dfs(day + 1, max(oi - b[day], 0ll), whk + a[day]);
    dfs(day + 1, oi + c[day], max(whk - d[day], 0ll));
}
signed main() {
    scanf("%lld", &n);
    for (register int i = 1; i <= n; ++i) scanf("%lld%lld%lld%lld", &a[i], &b[i], &c[i], &d[i]);
    dfs(0, 0, 0);
    printf("%lld\n", ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T2任

在画板上有一片黑白相间的矩形区域满足这样的性质:如果认为相同颜色的方块可以在上下左右四个方向连通,那么任意两个黑色方块要么不连通,要么连通但之间只有一条简单路径(不重复经过同一个格子的路径)。
这个矩形区域有\(N\)\(M\)列, 从上到下依次为第\(1, 2, 3... N-1, N\)行, 从左到右依次为第\(1, 2, 3... M-1, M\)列。
每次郭神会询问这片矩形区域内的一个子矩形。在只考虑这个子矩形内的像素时(即从子矩形内部不能和子矩形之外的像素相连通),问这个子矩形内的黑色方块组成了多少连通块。
保证任意两个黑色像素之间最多只有一条简单路径。
## 咋做
注意审题
注意审题
注意审题
题里说了联通的部分是一棵树。所以整张图是一个森林,那么题目的询问转化为了这个范围内有多少颗树。树有什么性质呢?众所周知树有\(n\)个点,\(n-1\)条边。当我们已知点数和边数,树的棵数就是点数减边数。
树的点数、边数可以\(O(nm)\)预处理,再利用二维前缀和便可以做到\(O(1)\)查询。整体复杂度\(O(nm + q)\)

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e3 + 5;
char mp[MAXN][MAXN];
bool v[MAXN][MAXN];
int sum1[MAXN][MAXN], sum2[MAXN][MAXN], sum3[MAXN][MAXN];
int step[][2] = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } };
int n, m, q;

inline void bfs() {
    queue<pair<int, int>> q;
    for (register int i = 1; i <= n; ++i)
        for (register int j = 1; j <= m; ++j) {
            if (mp[i][j] == '1' && !v[i][j]) {
                q.push(make_pair(i, j));
                while (q.size()) {
                    int x = q.front().first, y = q.front().second;
                    int xi, yi;
                    v[x][y] = true;
                    q.pop();
                    for (register int i = 0; i <= 0; ++i) {
                        xi = x;
                        yi = y + step[i][1];
                        if (mp[xi][yi] == '1' && !v[xi][yi] && xi >= 1 && xi <= n && yi >= 1 && yi <= m) {
                            ++sum3[x][y];
                            q.push(make_pair(xi, yi));
                        }
                    }
                }
            }
        }
    memset(v, 0, sizeof(v));
    for (register int i = 1; i <= n; ++i)
        for (register int j = 1; j <= m; ++j) {
            if (mp[i][j] == '1' && !v[i][j]) {
                q.push(make_pair(i, j));
                while (q.size()) {
                    int x = q.front().first, y = q.front().second;
                    int xi, yi;
                    v[x][y] = true;
                    q.pop();
                    for (register int i = 2; i <= 2; ++i) {
                        xi = x + step[i][0];
                        yi = y;
                        if (mp[xi][yi] == '1' && !v[xi][yi] && xi >= 1 && xi <= n && yi >= 1 && yi <= m) {
                            ++sum2[x][y];
                            q.push(make_pair(xi, yi));
                        }
                    }
                }
            }
        }
}
inline void test(int S[][MAXN]) {
    cerr << "-----##-----" << endl;
    for (register int i = 1; i <= n; ++i) {
        for (register int j = 1; j <= m; ++j) cerr << S[i][j] << " ";
        cerr << endl;
    }
}
inline void sum(int S[][MAXN]) {
    for (register int i = 1; i <= n; ++i)
        for (register int j = 1; j <= m; ++j) S[i][j] = S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1] + S[i][j];
}
inline int calc(int s[][MAXN], const int &a, const int &b, const int &c, const int &d) {
    register int t = s[c][d] + s[a - 1][b - 1] - s[c][b - 1] - s[a - 1][d];
    return t;
}
int main() {
#ifdef lky233
    freopen("duty.in", "r", stdin);
    freopen("duty.out", "w", stdout);
#endif
    scanf("%d %d %d", &n, &m, &q);
    for (register int i = 1; i <= n; ++i) scanf("%s", mp[i] + 1);
    bfs();
    for (register int i = 1; i <= n; ++i)
        for (register int j = 1; j <= m; ++j)
            sum1[i][j] = sum1[i - 1][j] + sum1[i][j - 1] - sum1[i - 1][j - 1] + (mp[i][j] - '0');
    sum(sum2);
    sum(sum3);
    for (register int i = 1, a, b, c, d; i <= q; ++i) {
        scanf("%d %d %d %d", &a, &b, &c, &d);
        printf("%d\n", calc(sum1, a, b, c, d) - calc(sum2, a, b, c - 1, d) - calc(sum3, a, b, c, d - 1));
    }
}

T3飞

空间限制 \(32MB\)
liu_runda 决定提高一下知识水平,于是他去请教郭神.郭神随手就给了 liu_runda 一道神题,liu_runda 并不会做,于是把这个题扔到联考里给高二的做。
郭神有\(n\)条位于第一象限内的线段,给出每条线段与\(x\)轴和\(y\)轴交点的坐标,显然这样就可以唯一确定每一条线段。
\(n\)条线段和\(y\)轴交点的纵坐标分别为\(1,2,3,4...n\)。我们记和\(y\)轴交点纵坐标为\(i\)的线段和\(x\)轴交点的横坐标为\(x[i] + 1\)按这样的方式生成:
\(x[1]\)由输入给出。\[x[i] = (x[i-1]+a),2 \leq i \leq n\]即:如果\(x[3] = 4\),则与\(y\)轴交点纵坐标为\(3\)的抛物线和\(x\)轴交点的横坐标为\(4+1=5\)
我们保证给出的\(n,x[1],a,mod\)使得所有的\(x[1]\)互不相同。
对于第一象限内的所有点(点的横纵坐标可以是任意实数),如果一个点被\(x\)条线段经过, 它的鬼畜值就是\(\frac {x \times (x-1)}{2}\)
第求一象限内的所有点的鬼畜值之和。

怎么做呢

首先分析这个有些特点的算式。我们发现如果两条线相交,会产生1的贡献,而如果三条线交于一点,会产生3的贡献,与三条线分别相交等价,以此类推,这个题被转化为求出现了几次两条线相交的情况。
由于线在y轴每次加一,每条线只会与x比它大的线相交。至此,这个问题转化为了一维的问题。
之后的推理有两种思路。


按照插入时间来对x排序,我们发现,当x与其余x逆序时,会产生他前面的线个数的贡献,这样这个问题就变成了求逆序对的问题。
但是\(mod \leq 10^8\),直接做是放不下的。离散化?\(n \leq 10^7\),如果开一个\(1e7\)的数组空间依旧爆炸。
分析\(x\)这个数列,在\(x\)被取模之前,这一段区间是递增的,这段区间内不会产生贡献,而它会与上一个区间对应位置之后的数产生贡献,对区间内的每个数来说,这个贡献成等差数列,可以快速进行处理。
对应位置产生的贡献,由于每段区间是规律的,因此我们仅记录每段区间起始位置的\(x\)形成的逆序对个数,每个逆序对将会产生该区间长度的贡献。由于\(x1\)的限制,第一段并不完整, 需要单独计算。\(a\)的范围是\(1e5\),可以轻松通过此题。
感谢 \(@jiqimao\)大佬提供了这个优秀的算法。


这个是我的差劲算法:
按照\(x\)的位置进行排序,我们发现每个\(x\)所产生的贡献是他后边x的个数。同样的在取模前这一段内,\(x\)递增,而每次\(x+a\)后,排在\(x\)后的数将会减去之前的区间段数。因此一段中\(x\)的贡献是等差数列,循环每一段,就可以得到答案。

劣质代码

等差数列边界处理太麻烦调完再发。这个是暴力跑的

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e5 + 5;
int A[MAXN];
int a, mod, x1, n;
long long ans;
inline void add(int x) {
    for (; x <= a; x += x & (~x + 1)) A[x] += 1;
}
inline int ask(int x) {
    int ans = 0;
    for (; x; x -= x & (~x + 1)) ans += A[x];
    return ans;
}
inline int calc(int a1, int d, int n) { return a1 * n + (n * (n - 1)) * d; }
signed main() {
    cin >> n >> x1 >> a >> mod;

    for (register int i = 1, now = x1, cnt = 0, lun = 0; i <= n; ++i) {
        if (now >= a) {
            cnt -= lun;
            if (x1 > now)
                ++cnt;
            ans += cnt;
        } else
            cnt = i - 1 - ask(now + 1), ans += cnt, add(now + 1);
        now += a;
        if (now >= mod)
            now -= mod, ++lun;
    }
    cout << ans << endl;
}

猜你喜欢

转载自www.cnblogs.com/Shiina-Rikka/p/11584458.html