[AtCoder]AtCoder Regular Contest 082

C - Together

题意

给定$N$个数,求它们或加一或减一或不变的众数。

题解

没什么好说的。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+9;
int cnt[N];
inline int read() {
    int s = 1, a = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
    return s * a;
}
int main() {
    int n = read();
    for (int i = 1; i <= n; i++) {
        int ai = read();
        cnt[ ai ]++, cnt[ ai+1 ]++, cnt[ ai-1 ]++;
    }
    int res = 0;
    for (int i = 0; i <= 1e5; i++) res = max(res, cnt[i]);
    return 0 * printf("%d\n", res);
}

D - Derangement

题意

给定$N$个数的一个排列,每次只能交换相邻两数,求最少的交换次数使得$p_i\neq i$。

题解

模拟一下,如果碰到$p_i=i$就把它和后面一个数交换。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+9;
int p[N];
inline int read() {
    int s = 1, a = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
    return s * a;
}
int main() {
    int n = read();
    for (int i = 1; i <= n; i++) p[i] = read();
    int res = 0;
    for (int i = 1; i < n; i++) if (p[i] == i) res++, swap(p[i], p[i + 1]);
    if (p[n] == n) res++;
    return 0 * printf("%d\n", res);
}

E - ConvexScore

题意

给定$N$个点$(x_i,y_i)$,定义这$N$个点的子集$S$为“凸多边形集”当且仅当$S$中所有点恰好构成一个凸多边形。对一个“凸多边形集”$S$,定义它的$score$为$2^{n-|S|}$,其中$n$为$S$构成的凸多边形包含(包括边上)的$N$个点中的点数。求所有“凸多边形集”的$score$之和对$998244353$取模的结果。

题解

题目要我们求的$2^{n-|S|}$对应的是一种集合取点的方案。
我们考虑这$N$个点的任意子集$X$要被计算$score$,当且仅当它的凸包面积为正。
再进一步,计算时任意凸包面积为正的子集对$score$的贡献为$1$,因为你把凸包上的点作为“凸多边形集”,其余的点作为一种取点的方案,恰好对应了“凸多边形集”的所有取点方案。
然后我们就只要从$2^N$中把能组成直线的点集去掉。(注意不要重复算单点和空集的情况)
去除共线的点集时,我们考虑枚举$N^2$个点对组成的线段,先把斜率化成最简分数$hash$一下排个序,然后对每个线段并查集维护即可。时间复杂度:$\Theta (N^2logN)$。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Q = 998244353;
const int N = 209;
ll x[N], y[N], p[N], f[N], size[N];
inline int read() {
    int s = 1, a = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
    return s * a;
}
inline int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}
inline int findSet(int x) {
    return f[x] == x ? x : f[x] = findSet(f[x]);
}
inline void unionSet(int x, int y) {
    int fx = findSet(x), fy = findSet(y);
    if (fx != fy) {
        f[fy] = fx;
        size[fx] += size[fy];
    }
}
struct Seg {
    int p, q; ll hash;
    bool operator < (const Seg &rhs) const {
        return hash < rhs.hash;
    }
};
vector<Seg> seg;
int main() {
    p[0] = 1;
    for (int i = 1; i < N; i++) {
        p[i] = p[i - 1] << 1, p[i] %= Q;
    }
    int n = read();
    for (int i = 1; i <= n; i++) {
        x[i] = read(), y[i] = read();
    }
    ll res = (p[n] - n - 1) % Q;
    for (int i = 1; i < n; i++) {
        for (int j = i + 1; j <= n; j++) {
            int dx = x[i] - x[j], dy = y[i] - y[j], g = gcd(dx, dy);
            dx /= g, dy /= g;
            if (dx == 0) dy = 1;
            if (dy == 0) dx = 1;
            if (dx < 0) dx *= -1, dy *= -1;
            Seg s; s.p = i, s.q = j, s.hash = dx * 100000 + dy;
            seg.push_back(s);
        }
    }
    sort(seg.begin(), seg.end());
    for (int i = 0, j = 0; i < seg.size(); i = j) {
        for (int k = 1; k <= n; k++) {
            f[k] = k, size[k] = 1;
        }
        for (; j < seg.size() && seg[i].hash == seg[j].hash; j++) {
            unionSet(seg[j].p, seg[j].q);
        }
        for (int k = 1; k <= n; k++) {
            if (f[k] == k && size[k] >= 2) {
                res -= (p[ size[k] ] - size[k] - 1), res += Q, res %= Q;
            }
        }
    }
    printf("%lld\n", res);
    return 0;
}

F - Sandglass

题意

给定一个总容量为$X$的沙漏,初始状态$A$部分有$a$克沙子,$B$部分有$X-a$克沙子,每秒钟掉下来$1$克沙子。有$K$个$r_i$时刻会将沙漏倒置。现在有$Q$个询问$(t_i,a_i)$,初始$A$部分在上面且有沙子$a_i$克。求在$t_i$时刻$A$部分的沙子多少克。

题解

假设$A$部分沙子初始有$x$克,在$t$时刻时的沙子有$f_t(x)$克。
当某时刻$A$部分有$y$克沙子时,下一秒钟:
若$A$部分在上面,则$f_t(x)=g_1(y)=max(y-1,0)$;
若$A$部分在下面,则$f_t(x)=g_2(y)=max(y+1,X)$.
因为$f_t$是在函数$g_1,g_2$下封闭的,所以它一定是个如下的线性分段函数:
$\begin{equation}
f_t(x)=
\begin{cases}
a+c,&0\leq x\leq a\newline
x+c,&a\leq x\leq b\newline
b+c,&b\leq x\leq X
\end{cases}
\end{equation}$
对于每个$t$维护上式中的$a,b,c$,然后回答询问即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int K = 1e5+9;
ll r[K];
inline ll read() {
    ll s = 1, a = 0; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') s = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {a = a * 10 + ch - '0'; ch = getchar();}
    return s * a;
}
inline void turn(ll &v, ll x) {
    if (v < 0) v = 0;
    if (v > x) v = x;
}
int main() {
    ll x = read(), k = read();
    for (int i = 1; i <= k; i++) r[i] = read();
    ll q = read();
    ll low = 0, mid = 0, upp = x;
    int i = 1, sign = -1;
    while (q--) {
        ll t = read(), a = read();
        while (i <= k && r[i] <= t) {
            ll dta = (r[i] - r[i - 1]) * sign;
            low += dta, upp += dta, mid += dta;
            turn(low, x), turn(mid, x);
            i++, sign *= -1;
        }
        ll res = a + mid;
        ll dta = (t - r[i - 1]) * sign;
        if (res < low) res = low;
        if (res > upp) res = upp;
        res += dta, turn(res, x);
        printf("%lld\n", res);
    }
    return 0;
}
posted @ 2017-09-03 19:36  jstztzy  阅读(481)  评论(1编辑  收藏  举报