2022.9.16模拟赛总结

T1

思路

看到绝对值,就应该要想到正负性

假如只用算一种代价,显然要不都取最大的正数,要不取最小的负数

于是我们考虑每次给 \(a,b,c\) 分别取正负,加起来从大到小排序,取前 \(m\) 个数

一共有 \(2^3=8\) 种分类,每次做完都取最大值即可

代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline LL reads()
{
    LL sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
const LL t[8][3] = {{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {-1, 1, 1}, {1, -1, -1}, {-1, 1, -1}, {-1, -1, 1}, {-1, -1, -1}};
int n, m;
LL ans, a[1005], b[1005], c[1005], s[1005];
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(); m = reads();
    FOR(i, 1, n) a[i] = reads(), b[i] = reads(), c[i] = reads();
    FOR(i, 0, 7)
    {
        LL sum = 0;
        FOR(j, 1, n) s[j] = a[j] * t[i][0] + b[j] * t[i][1] + c[j] * t[i][2];
        std::sort(s + 1, s + 1 + n, [&](LL a, LL b){return a > b;});
        FOR(j, 1, m) sum += s[j];
        ans = std::max(ans, sum);
    }
    printf("%lld", ans);
    return 0;
}

T2

题目传送门

思路

显然,让相同字母尽量靠前是最优的选择

考虑设立状态 \(f[S]\) 表示 \(S\) 中的串一起建一棵字典树最小的结点数

则有转移:

\[f[S] = f[i]+f[S\oplus i]-same[S],~i\in S \]

其中 \(same[S]\) 表示 \(S\) 串中都有的共同字母的个数,这个可以预处理出来

时间复杂度为 \(O(3^n+w 2^n)\),其中 \(w=26\)

代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
int n;
char s[1000005];
int f[1 << 16], g[1 << 16][26];
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    scanf("%d", &n);
    FOR(i, 1, n)
    {
        scanf("%s", s + 1); int len = strlen(s + 1);
        f[1 << (i - 1)] = len;
        FOR(j, 1, len) g[1 << (i - 1)][s[j] - 'a']++;
    }
    FOR(i, 1, (1 << n) - 1)
    {
        if (__builtin_popcount(i) == 1)
            continue;
        FOR(k, 0, 25) g[i][k] = 1e6;
        FOR(j, 1, n) if(i & (1 << (j - 1)))
            FOR(k, 0, 25) g[i][k] = std::min(g[i][k], g[1 << (j - 1)][k]);
    }
    FOR(i, 1, (1 << n) - 1)
    {
        if(f[i]) continue;
        f[i] = 1e6;
        int dec = 0;
        FOR(k, 0, 25) dec += g[i][k];
        for(int j = (i - 1) & i; j; j = (j - 1) & i)
        {
            int sum = f[j] + f[i ^ j] - dec;
            if(sum < f[i]) f[i] = sum;
        }
    }
    printf("%d", f[(1 << n) - 1] + 1);
    return 0;
}

T3

题目传送门

思路

一道比较简单的状压容斥(虽然有小傻瓜要金牌爷点拨才会做...)

首先这道题要会两个三角形怎么求交

如果有 \((x_1,y_1,r_1),(x_2,y_2,r_2)\),那我们先算出 \((x_0=\max(x_1, x_2), y_0=\max(y_1, y_2), r_0)\),其中 \(r_0=\min(x_1+r_1+y_1, x_2+r_2+y_2)-y_0-x_0\)

如果 \(r_0\le 0\),说明三角形没有交;反之,\((x_0,y_0,r_0)\) 就是交三角形

考虑状压,\(f[S]\) 表示 \(S\) 中三角形的交的面积;\(g[i]\) 表示至少 \(i\) 个三角形的交的面积和

由至少算恰好,就是二项式反演的套路

代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n;
struct Tri
{
    int x, y, r;
}p[1 << 10];
double ans, f[15];
int t = 1;
inline bool in(Tri a, int x, int y)
{
    if(x < a.x || x > a.x + a.r || y < a.y || y > a.y + a.r) return 0;
    if(x > a.x + a.r - (y - a.y)) return 0;
    return 1;
}
inline double S(Tri a) {return 1.0 * a.r * a.r / 2.0;}
inline double C(int n, int m)
{
    double re = 1.0;
    FOR(i, 1, m) re = re * (n - i + 1);
    FOR(i, 1, m) re = re / i;
    return re;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads();
    FOR(i, 1, n)
    {
        int x = reads(), y = reads(), r = reads();
        p[1 << (i - 1)] = (Tri){x, y, r};
    }
    FOR(i, 1, (1 << n) - 1)
    {
        if (__builtin_popcount(i) > 1)
        FOR(j, 1, n) if(i & (1 << (j - 1)))
        {
            int h = (1 << (j - 1));
            if(!p[h].r || !p[i ^ h].r) continue;
            int x = std::max(p[h].x, p[i ^ h].x), y = std::max(p[h].y, p[i ^ h].y);
            if(in(p[h], x, y) && in(p[i ^ h], x, y))
            {
                p[i].x = x, p[i].y = y;
                p[i].r = std::min(p[h].x + p[h].r - (y - p[h].y), p[i ^ h].x + p[i ^ h].r - (y - p[i ^ h].y)) - x;
            }
            break;
        }
        f[__builtin_popcount(i)] += S(p[i]);
    }
    ROF(i, n - 1, 1) ROF(j, n, i + 1)
        f[i] -= f[j] * C(j, i);
    FOR(i, 1, n)
        if(i & 1) ans += f[i];
    printf("%.1lf", ans);
    return 0;
}

T4

传送门

思路

包含分离,符合树的结构:如果矩形 \(a\) 包含矩形 \(b\),那么就从 \(a\)\(b\) 连一条边

考虑离散化,然后将矩形拆为左右 \(x\) 排序,点按照 \(x\) 排序

开一棵线段树,结点 \([l,r]\) 代表 \(y\)\([l,r]\) 区间目前是被哪个矩阵覆盖

当加入一个左 \(x\) 时,我们更新线段树上对应高度的编号,并由上一个这段区间的编号指向当前的编号

当加入一个点 \((x,y)\) 时,我们查找线段树上 \(y\) 的位置是哪个矩形覆盖,将颜色加入到这个矩阵的 set

当加入一个右 \(x\) 时,代表这个矩阵的结束,我们将线段树上对应高度重新覆盖为当前矩阵的父亲的编号

建好树后,我们考虑用 dsu on tree,递归到叶子后回溯,将儿子的颜色集合与当前点的集合进行启发式合并;然后统计当前点的集合内元素个数即可

代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, m, fa[80005];
int s1[240005], s2[240005], c1, c2;
struct Line
{
    int x, d, u, id, ty;
}a[160005];
struct Point
{
    int x, y, c;
}b[80005];
std::vector<int> e[80005];
std::set<int> p[80005];
namespace Seg
{
    #define ls (now << 1)
    #define rs ((now << 1) | 1)
    int tr[960005], tag[960005];
    inline void dn(int now)
    {
        if(~tag[now])
        {
            tr[ls] = tr[rs] = tag[ls] = tag[rs] = tag[now];
            tag[now] = -1;
        }
    }
    void modify(int now, int l, int r, int L, int R, int id, bool ty)
    {
        if(L <= l && r <= R)
        {
            if(ty && fa[id] == -1)
                fa[id] = tr[now],
                e[tr[now]].emplace_back(id);
            tr[now] = tag[now] = id;
            return;
        }
        int mid = (l + r) >> 1; dn(now);
        if(L <= mid) modify(ls, l, mid, L, R, id, ty);
        if(mid < R) modify(rs, mid + 1, r, L, R, id, ty);
        tr[now] = tr[ls] == tr[rs] ? tr[ls] : -1;
    }
    void query(int now, int l, int r, int to, int cl)
    {
        if(tr[now] > 0) return void(p[tr[now]].insert(cl));
        if(l == r) return;
        int mid = (l + r) >> 1; dn(now);
        if(to <= mid) query(ls, l, mid, to, cl);
        else query(rs, mid + 1, r, to, cl);
    }
}
int ans[80005];
void dfs(int now)
{
    for(int to : e[now])
    {
        dfs(to);
        if(p[now].size() < p[to].size()) std::swap(p[now], p[to]);
        p[now].insert(p[to].begin(), p[to].end());
    }
    ans[now] = p[now].size();
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads();
    FOR(i, 1, n)
    {
        fa[i] = -1;
        int l = reads(), d = reads(), r = reads(), u = reads();
        s1[++c1] = l, s1[++c1] = r, s2[++c2] = d, s2[++c2] = u;
        a[(i << 1) - 1] = (Line){l, d, u, i, 0};
        a[i << 1] = (Line){r, d, u, i, 1};
    }
    FOR(i, 1, m)
        b[i] = (Point){reads(), reads(), reads()},
        s1[++c1] = b[i].x, s2[++c2] = b[i].y;
    std::sort(s1 + 1, s1 + 1 + c1), std::sort(s2 + 1, s2 + 1 + c2);
    c1 = std::unique(s1 + 1, s1 + 1 + c1) - s1 - 1;
    c2 = std::unique(s2 + 1, s2 + 1 + c2) - s2 - 1;
    FOR(i, 1, n << 1)
    {
        a[i].x = std::lower_bound(s1 + 1, s1 + 1 + c1, a[i].x) - s1;
        a[i].d = std::lower_bound(s2 + 1, s2 + 1 + c2, a[i].d) - s2;
        a[i].u = std::lower_bound(s2 + 1, s2 + 1 + c2, a[i].u) - s2;
    }
    FOR(i, 1, m)
    {
        b[i].x = std::lower_bound(s1 + 1, s1 + 1 + c1, b[i].x) - s1;
        b[i].y = std::lower_bound(s2 + 1, s2 + 1 + c2, b[i].y) - s2;
    }
    std::sort(a + 1, a + 1 + (n << 1), [&](Line a, Line b){return a.x == b.x ? a.ty < b.ty : a.x < b.x;});
    std::sort(b + 1, b + 1 + m, [&](Point a, Point b){return a.x < b.x;});
    int pos1 = 1, pos2 = 1;
    FOR(i, 1, c1)
    {
        while(pos1 <= (n << 1) && a[pos1].x == i && !a[pos1].ty)
        {
            Seg::modify(1, 1, c2, a[pos1].d, a[pos1].u, a[pos1].id, 1);
            pos1++;
        }
        while(pos2 <= m && b[pos2].x == i)
        {
            Seg::query(1, 1, c2, b[pos2].y, b[pos2].c);
            pos2++;
        }
        while(pos1 <= (n << 1) && a[pos1].x == i && a[pos1].ty)
        {
            Seg::modify(1, 1, c2, a[pos1].d, a[pos1].u, fa[a[pos1].id], 0);
            pos1++;
        }
    }
    for(int to : e[0]) dfs(to);
    FOR(i, 1, n) printf("%d\n", ans[i]);
    return 0;
}
posted @ 2022-09-16 15:22  zuytong  阅读(15)  评论(0编辑  收藏  举报