T1圆圈游戏
暴力DP有60分,设包含圆i的最小的圆是fa[i],那没最终会的得到一棵树,对于一棵子树,选了根节点就不能选子树内其它点,f[i]=max(w[i],\(\sum f[son]\)).
瓶颈就在怎么建图,因为圆不相交相切,所以扫描线的时候相对位置不会发生改变,用set维护一下就好啦。

#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<cstdio>
#include<set>
using namespace std;
int n, ans, tmp, lx;
const int N = 200010;
int f[N], fa[N];
struct yuan 
{
    int x, y, r, w;
    double h(int dir) {return y + dir * sqrt(1.0 * r * r - (1.0 * lx - x) * (lx - x));}
} y[N];
struct hu 
{
    int dir, id;
    friend bool operator <(const hu &a, const hu &b)
    {return a.id == b.id ? a.dir < b.dir : (y[a.id].h(a.dir) < y[b.id].h(b.dir));}
} b[N];
inline int read() 
{
    int res = 0; char ch = getchar(); bool XX = false;
    for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
    for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
    return XX ? -res : res;
}
int my(hu a, hu b) {return y[a.id].x - a.dir * y[a.id].r < y[b.id].x - a.dir * y[b.id].r;}
set<hu>s;
set<hu>::iterator it;
signed main() 
{
    cin >> n;
    for (int i = 1; i <= n; ++i) 
    {
        y[i].x = read(); y[i].y = read(); y[i].r = read(); y[i].w = read();
        b[++tmp] = (hu) {1, i}, b[++tmp] = (hu) { -1, i};
    }
    sort(b + 1, b + 1 + tmp, my);
    for (int i = 1; i <= tmp; ++i) 
    {
        int id = b[i].id, dir = b[i].dir;
        lx = y[id].x - dir * y[id].r;
        if (dir == 1) 
        {
            if (!s.empty()) 
            {
                it = s.upper_bound(b[i]);
                if (it != s.end())
                    fa[id] = (it->dir == 1) ? it->id : fa[it->id];
            }
            s.insert((hu) {1, id}); s.insert((hu) { -1, id});
        } else 
        {
            s.erase((hu) {1, id}); s.erase((hu) { -1, id});
            f[fa[id]] += max(f[id], y[id].w);
        }
    }
    cout << f[0];
    return 0;
}

T2划分序列
很明显的二分答案,关键就在于check。
设f1[i]表示在i处划分一下,在满足mid的条件下最少划分次数,f2[i]表示最多的划分次数,若\(f1[n] \le k \le f2[n]\)则mid合法。
暴力dp是\(O(n^2)\)的,发现更新f1,f2的是区间最大/最小值,用树状数组维护一下就可以了。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
int n, k, ans, tot;
const int N = 100010, inf = 1 << 30;
int a[N], f1[N], f2[N], tr1[N], tr2[N], b[N], t1[N], t2[N];
inline int read() 
{
    int res = 0; char ch = getchar(); bool XX = false;
    for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
    for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
    return XX ? -res : res;
}
inline int lowbit(int x) {return x & (-x);}
void change1(int pos, int val) 
{
    for (; pos; pos -= lowbit(pos))tr1[pos] = min(tr1[pos], val);
}
int ask1(int pos) 
{
    int res = inf;
    for (; pos <= tot; pos += lowbit(pos))res = min(res, tr1[pos]);
    return res;
}
void change2(int pos, int val) 
{
    for (; pos; pos -= lowbit(pos))tr2[pos] = max(tr2[pos], val);
}
int ask2(int pos) 
{
    int res = -inf;
    for (; pos <= tot; pos += lowbit(pos))res = max(res, tr2[pos]);
    return res;
}
int check2(int mid) 
{
    tot = 0;
    for (int i = 0; i <= n; ++i) 
    {
        b[++tot] = a[i];
        b[++tot] = a[i] - mid;
    }
    sort(b + 1, b + 1 + tot);
    tot = unique(b + 1, b + 1 + tot) - b - 1;
    for (int i = 0; i <= n; ++i) 
    {
        t1[i] = lower_bound(b + 1, b + 1 + tot, a[i]) - b;
        t2[i] = lower_bound(b + 1, b + 1 + tot, a[i] - mid) - b;
    }
    for (int i = 1; i <= tot; ++i)tr1[i] = inf, tr2[i] = -inf;
    change1(t1[0], 0); change2(t1[0], 0);
    for (int i = 1; i <= n; ++i) 
    {
        f1[i] = ask1(t2[i]) + 1; f2[i] = ask2(t2[i]) + 1;
        change1(t1[i], f1[i]); change2(t1[i], f2[i]);
    }
    return f1[n] <= k && k <= f2[n];
}
void solve2() 
{
    int l = 0, r = 0;
    for (int i = 1; i <= n; ++i)
        if (a[i] < 0)l += a[i];
        else r += a[i];
    for (int i = 1; i <= n; ++i)a[i] += a[i - 1];
    while (l <= r) 
    {
        int mid = (l + r) / 2;
        if (check2(mid))ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << ans;
}
int main() 
{
    cin >> n >> k;
    for (int i = 1; i <= n; ++i)a[i] = read();
    solve2();
    return 0;
}

T3生成树求和
由于加法不进位,所以每一位我们单独考虑。
用矩阵树定理求的是\(\sum\)所有生成树边权的积,而这里我们需要求的是边权和,所以三次单位根的乘法来完成加法。
最后还要求解一下三元一次方程,可以选择高斯消元,也可以像我一样手动消元。

#include<iostream>
#include<cstring>
#include<cstdio>
#define int long long
#define LL long long
using namespace std;
int n, m, ans;
const LL N = 105, mod = 1e9 + 7, inv2 = (mod + 1) >> 1, inv3 = (mod + 1) / 3, sqrt3 = 82062379;
int fr[N * N], to[N * N], val[N * N], bin[20];
inline int read() 
{
    int res = 0; char ch = getchar(); bool XX = false;
    for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
    for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
    return XX ? -res : res;
}
LL ksm(LL a, LL b, LL mod) 
{
    LL res = 1;
    for (; b; b >>= 1, a = a * a % mod)
        if (b & 1)res = res * a % mod;
    return res;
}
struct xu 
{
    LL a, b;
    xu(int _a = 0, int _b = 0) {a = _a, b = _b;}
    friend xu operator +(const xu &x, const xu &y)
    {return (xu) {(x.a + y.a) % mod, (x.b + y.b) % mod};}
    friend xu operator -(const xu &x, const xu &y)
    {return (xu) {(x.a - y.a + mod) % mod, (x.b - y.b + mod) % mod};}
    friend xu operator *(const xu &x, const xu &y)
    {return (xu) {(x.a * y.a % mod - x.b * y.b % mod + mod) % mod, (x.a * y.b + x.b * y.a) % mod};}
    friend xu operator *(const xu &x, const int &y)
    {return (xu) {(x.a * y) % mod, (x.b * y) % mod};}
    friend xu operator /(const xu &x, const int &y) 
    {
        int inv = ksm(y, mod - 2, mod);
        return (xu) {(x.a * inv) % mod, (x.b * inv) % mod};
    }
    friend xu operator /(const xu &x, const xu &y) 
    {
        return x * (xu) {y.a, mod - y.b} / ((y.a * y.a % mod + y.b * y.b % mod) % mod);
    }
} w[3], invw[3], a[N][N], coef[3], inv;
xu work() 
{
    xu res = xu(1, 0);
    for (int i = 1; i < n; ++i) 
    {
        for (int j = i; j < n; ++j)
            if (a[j][i].a || a[j][i].b) 
            {
                if (i == j)break;
                swap(a[i], a[j]); res = res * (mod - 1);
                break;
            }
        for (int j = i + 1; j < n; ++j) 
        {
            inv = a[j][i] / a[i][i];
            for (int k = i; k < n; ++k)a[j][k] = a[j][k] - inv * a[i][k];
        }
        res = res * a[i][i];
    }
    return res;
}
void solve(xu *p) 
{
    xu tmp[3];
    tmp[0] = p[0]; tmp[1] = p[1]; tmp[2] = p[2];
    p[0] = tmp[0] + tmp[1] + tmp[2];
    p[1] = tmp[0] + tmp[1] * invw[1] + tmp[2] * invw[2];
    p[2] = tmp[0] + tmp[1] * invw[2] + tmp[2] * invw[1];
    for (int i = 0; i <= 2; ++i)p[i] = p[i] * inv3;
}
LL suan(int p) 
{
    int k;
    xu w0, w1, w2, c;
    for (int i = 0; i <= 2; ++i) 
    {
        memset(a, 0, sizeof(a));
        w0 = xu(1, 0); w1 = w[i]; w2 = w[(i + i) % 3];
        for (int j = 1; j <= m; ++j) 
        {
            k = val[j] / bin[p] % 3;
            c = (k == 1 ? w1 : (k == 2 ? w2 : w0));
            a[fr[j]][fr[j]] = a[fr[j]][fr[j]] + c;
            a[to[j]][to[j]] = a[to[j]][to[j]] + c;
            a[fr[j]][to[j]] = a[fr[j]][to[j]] - c;
            a[to[j]][fr[j]] = a[to[j]][fr[j]] - c;
        }
        coef[i] = work();
    }
    solve(coef);
    return ((coef[1].a + coef[2].a + coef[2].a) % mod) * bin[p] % mod;
}
signed main() 
{
    freopen("sum.in", "r", stdin);
    freopen("sum.out", "w", stdout);
    cin >> n >> m;
    w[0] = xu(1, 0); w[1] = xu(mod - inv2, sqrt3 * inv2 % mod);
    w[2] = xu(mod - inv2, mod - sqrt3 * inv2 % mod);
    invw[0] = xu(1, 0) / w[0]; invw[1] = xu(1, 0) / w[1]; invw[2] = xu(1, 0) / w[2];
    for (int i = 1; i <= m; ++i)fr[i] = read(), to[i] = read(), val[i] = read();
    bin[0] = 1;
    for (int i = 1; i <= 10; ++i)bin[i] = bin[i - 1] * 3;
    for (int i = 0; i <= 10; ++i)(ans += suan(i)) %= mod;
    cout << ans;
    return 0;
}