胡测6 (by Delov/lxhcr)

T1 数正方体

假设三视图中出现的正方形个数分别为 \(a,b,c\) ,容易想到当 \(a>bc\)\(b>ac\)\(c>ab\) 时,一定不存在满足条件的立体结构,测试大样例发现这是一个充要条件,因此我们需要求解的是:

\[\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C[ij<k]+\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C[ik<j]+\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C[jk<i] \]

以第一部分为例,简单推导有

\[\begin{aligned} \sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C[ij<k] &=\sum_{i=1}^A\sum_{j=1}^B\max(0,C-ij)\\ \end{aligned} \]

容易发现 \(\max(ij)=AB\) ,显然这个数量级比 \(C\) 大,因此考虑用 \(C\) 限制 \(ij\) ,不妨设 \(i<j\) ,那么原式为:

\[\begin{aligned} \sum_{i=1}^{\min(A,\lfloor\sqrt{C}\rfloor)}\sum_{j=i+1}^{\min(B,\lfloor\tfrac{C}{i}\rfloor)}C-ij \end{aligned} \]

直接 \(O(\sqrt C)\) 计算即可。

code

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod = 998244353, inv2 = mod + 1 >> 1;
int T;
long long A, B, C;
int Calc ( long long x, long long y, long long t )
{
    int res = 0;
    int lim = min(x, (long long)sqrt(t));
    for ( int i = 1; i <= lim; i ++ )
    {
        long long L = i, R = min(y, t / i);
        if ( L <= R )
        {
            int cnt = ( R - L ) % mod, sumL = ( 1 + L ) % mod * ( L % mod ) % mod * inv2 % mod, sumR = ( 1 + R ) % mod * ( R % mod ) % mod * inv2 % mod;
            res = ( res + 1LL * cnt * ( t % mod ) % mod - 1LL * ( sumR - sumL + mod ) * i % mod + mod ) % mod;
        }
    }
    lim = min(y, (long long)sqrt(t));
    for ( int i = 1; i <= lim; i ++ )
    {
        long long L = i, R = min(x, t / i);
        if ( L <= R )
        {
            int cnt = ( R - L ) % mod, sumL = ( 1 + L ) % mod * ( L % mod ) % mod * inv2 % mod, sumR = ( 1 + R ) % mod * ( R % mod ) % mod * inv2 % mod;
            res = ( res + 1LL * cnt * ( t % mod ) % mod - 1LL * ( sumR - sumL + mod ) * i % mod + mod ) % mod;
        }
    }
    lim = min(min(x, y), (long long)sqrt(t));
    for ( int i = 1; i <= lim; i ++ )
        res = ( res + ( t - 1LL * i * i ) % mod ) % mod;
    return res;
}
void Work ()
{
    scanf("%lld%lld%lld", &A, &B, &C);
    int ans = ( A % mod ) * ( B % mod ) % mod * ( C % mod ) % mod;
    ans = ( ans - Calc(A, B, C) + mod ) % mod;
    ans = ( ans - Calc(A, C, B) + mod ) % mod;
    ans = ( ans - Calc(B, C, A) + mod ) % mod;
    printf("%d\n", ans);
    return;
}
int main ()
{
    freopen("cube.in", "r", stdin);
    freopen("cube.out", "w", stdout);
    scanf("%d", &T);
    while ( T -- )
        Work();
    return 0;
}

T2 数树上点

很容易有一个朴素的 dp 想法,设 \(f_{u,i}\) 表示以 \(u\) 为根的子树,最浅点距离 \(u\)\(i\) 时的方案数,转移时枚举 \(f_{u,i}\)\(f_{v,j}\)\(i+j+1\ge D\) 时更新 \(f_{u,\min(i,j+1)}\) 即可。

考虑进行优化,对上述 dp 做后缀和,设以 \(i\) 为根的 dp 数组的长度为 \(len_i\) ,容易发现转移可以在 \(O(\min(len_u,len_v+1))\) 的时间复杂度内完成,因此先做一次长链剖分可以做到 \(O(n)\)

code

#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 2e6;
const int mod = 998244353;
int n, lim;
struct Node
    { int next, v; } edge[max1 * 2 + 5];
int head[max1 + 5], total;
int maxdeep[max1 + 5], deep[max1 + 5], father[max1 + 5], son[max1 + 5];
int in[max1 + 5], out[max1 + 5], dfs_clock;
int f[max1 + 5], g[max1 + 5], tmp[max1 + 5];
void Add ( int u, int v )
{
    edge[++total].v = v;
    edge[total].next = head[u];
    head[u] = total;
    return;
}
void Find_Long_Edge ( int now, int fa, int depth )
{
    maxdeep[now] = deep[now] = depth, father[now] = fa, son[now] = 0;
    for ( int i = head[now]; i; i = edge[i].next )
    {
        int v = edge[i].v;
        if ( v == fa )
            continue;
        Find_Long_Edge(v, now, depth + 1);
        if ( maxdeep[v] > maxdeep[now] )
            maxdeep[now] = maxdeep[v], son[now] = v;
    }
    return;
}
void Connect_Long_Edge ( int now )
{
    in[now] = out[now] = ++dfs_clock;
    if ( son[now] )
    {
        Connect_Long_Edge(son[now]);
        out[now] = out[son[now]];
    }
    for ( int i = head[now]; i; i = edge[i].next )
    {
        int v = edge[i].v;
        if ( v == father[now] || v == son[now] )
            continue;
        Connect_Long_Edge(v);
    }
    return;
}
void Dfs ( int now )
{
    if ( son[now] )
        Dfs(son[now]);
    f[in[now]] = 1;
    if ( in[now] + 1 <= out[now] )
        f[in[now]] = ( f[in[now]] + f[in[now] + 1] - 1 ) % mod;
    if ( in[now] + lim <= out[now] )
        f[in[now]] = ( f[in[now]] + f[in[now] + lim] ) % mod;
    g[in[now]] = f[in[now]];
    if ( in[now] + 1 <= out[now] )
        g[in[now]] = ( g[in[now]] + g[in[now] + 1] ) % mod;
    for ( int i = head[now]; i; i = edge[i].next )
    {
        int v = edge[i].v;
        if ( v == father[now] || v == son[now] )
            continue;
        Dfs(v);
        int minlen = out[v] - in[v] + 1;
        for ( int k = in[now]; k <= in[now] + minlen; k ++ )
            tmp[k] = f[k];
        for ( int k = in[v]; k <= out[v]; k ++ )
            f[in[now] + k - in[v] + 1] = ( f[in[now] + k - in[v] + 1] + f[k] ) % mod;
        for ( int k = in[now]; k <= in[now] + minlen; k ++ )
        {
            int L = max(lim - k + in[now] + in[v] - 1, k - in[now] + in[v] - 1);
            if ( L <= out[v] )
                f[k] = ( f[k] + 1LL * tmp[k] * g[L] ) % mod;
        }
        for ( int k = in[v]; k <= out[v]; k ++ )
        {
            int L = max(lim - k + in[now] + in[v] - 1, k - in[v] + in[now] + 2);
            if ( L <= out[now] )
                f[in[now] + k - in[v] + 1] = ( f[in[now] + k - in[v] + 1] + 1LL * f[k] * g[L] ) % mod;
        }
        for ( int k = in[now] + minlen; k >= in[now]; k -- )
        {
            g[k] = f[k];
            if ( k + 1 <= out[now] )
                g[k] = ( g[k] + g[k + 1] ) % mod;
        }
    }
    return;
}
int main ()
{
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    scanf("%d%d", &n, &lim);
    for ( int i = 1, u, v; i <= n - 1; i ++ )
        { scanf("%d%d", &u, &v); Add(u, v), Add(v, u); }
    Find_Long_Edge(1, 0, 0);
    Connect_Long_Edge(1);
    Dfs(1);
    int ans = 0;
    for ( int i = in[1]; i <= out[1]; i ++ )
        ans = ( ans + f[i] ) % mod;
    ans = ( ans + 1 ) % mod;
    printf("%d\n", ans);
    return 0;
}

T3 数区间集

我们定义一个区间是极大的当且仅当 \(a_{l-1},a_{r+1}\) 均不属于集合 \(\{a_l,a_{l+1},a_{l+2}...a_{r}\}\) ,容易发现统计极大的区间的个数会导致一些集合计算两次,但是这些集合所代表的的区间一定没有交集并且 \(r_1+1\ne l_2\) ,因此考虑减去这些集合的贡献,具体的,设 \(b_i\) 表示 \(i\) 前面第一个与 \(a_i\) 相同的数的位置,如果没有则 \(b_i=0\) ,考虑一组计算两次的区间 \([l_1,r_1]\)\([l_2,r_2]\) ,考虑在 \(l_2\) 的位置计算答案,容易发现一定满足 \([l_2,r_2]\) 中所有的 \(b\) 均不为 \(0\) 并且满足 \(\max(b_i)-\min(b_i)-(r_2-l_2)=0\) ,由于区间内不存在相同的数,因此 \(\max(b_i)-\min(b_i)-(r_2-l_2)\ge 0\) ,可以用线段树维护最小值及个数统计答案,考虑这样统计会减去 \(r_1+1=l_2\) 的贡献,但容易发现这样的区间一定满足区间内 \(b\) 值最大,减去这样的区间的贡献即可。

code

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int max1 = 5e5;
const int inf = 0x3f3f3f3f;
int n, a[max1 + 5], b[max1 + 5], pos[max1 + 5];
long long ans;
struct Bit_Tree
{
    #define lowbit(now) ( now & -now )
    int tree[max1 + 5];
    void Clear ()
    {
        for ( int i = 1; i <= n; i ++ )
            tree[i] = 0;
        return;
    }
    void Insert ( int now, int x )
    {
        while ( now <= n )
        {
            tree[now] += x;
            now += lowbit(now);
        }
        return;
    }
    int Query ( int now )
    {
        int res = 0;
        while ( now )
        {
            res += tree[now];
            now -= lowbit(now);
        }
        return res;
    }
    int Query ( int L, int R )
    {
        return Query(R) - Query(L - 1);
    }
}Tree1;
struct Data
{
    int min_val, count;
    Data () {}
    Data ( int __min_val, int __count )
        { min_val = __min_val, count = __count; }
    Data operator + ( const Data &A ) const
    {
        if ( min_val < A.min_val )
            return *this;
        else if ( min_val > A.min_val )
            return A;
        return Data(min_val, count + A.count);
    }
};
struct Segment_Tree
{
    #define lson(now) ( now << 1 )
    #define rson(now) ( now << 1 | 1 )
    struct Struct_Segment_Tree
        { Data d; int lazy; } tree[max1 * 4 + 5];
    void Update ( int now, int x )
    {
        tree[now].d.min_val += x;
        tree[now].lazy += x;
        return;
    }
    void Push_Down ( int now )
    {
        if ( tree[now].lazy )
        {
            Update(lson(now), tree[now].lazy);
            Update(rson(now), tree[now].lazy);
            tree[now].lazy = 0;
        }
        return;
    }
    void Build ( int now, int L, int R )
    {
        tree[now].lazy = 0;
        if ( L == R )
            { tree[now].d = Data(-L, 1); return; }
        int mid = L + R >> 1;
        Build(lson(now), L, mid);
        Build(rson(now), mid + 1, R);
        tree[now].d = tree[lson(now)].d + tree[rson(now)].d;
        return;
    }
    void Insert ( int now, int L, int R, int ql, int qr, int x )
    {
        if ( L >= ql && R <= qr )
            return Update(now, x);
        Push_Down(now);
        int mid = L + R >> 1;
        if ( ql <= mid )
            Insert(lson(now), L, mid, ql, qr, x);
        if ( qr > mid )
            Insert(rson(now), mid + 1, R, ql, qr, x);
        tree[now].d = tree[lson(now)].d + tree[rson(now)].d;
        return;
    }
    Data Query ( int now, int L, int R, int ql, int qr )
    {
        if ( L >= ql && R <= qr )
            return tree[now].d;
        Push_Down(now);
        int mid = L + R >> 1;
        Data res = Data(inf, 0);
        if ( ql <= mid )
            res = res + Query(lson(now), L, mid, ql, qr);
        if ( qr > mid )
            res = res + Query(rson(now), mid + 1, R, ql, qr);
        return res;
    }
}Tree2;
int maxs[max1 + 5], mins[max1 + 5], maxtop, mintop;
int main ()
{
    freopen("seq.in", "r", stdin);
    freopen("seq.out", "w", stdout);
    scanf("%d", &n);
    for ( int i = 1; i <= n; i ++ )
        scanf("%d", &a[i]);
    for ( int i = 1; i <= n; i ++ )
        { b[i] = pos[a[i]]; pos[a[i]] = i; }
    Tree1.Clear();
    for ( int i = 1; i <= n; i ++ )
    {
        Tree1.Insert(i, 1);
        if ( b[i] )
            Tree1.Insert(b[i] + 1, -1);
        ans += Tree1.Query(b[i + 1] + 1, i);
    }
    Tree2.Build(1, 1, n);
    maxs[0] = mins[0] = n + 1;
    for ( int i = n; i >= 1; i -- )
    {
        if ( b[i] )
        {
            while ( maxtop && b[maxs[maxtop]] < b[i] )
            {
                Tree2.Insert(1, 1, n, maxs[maxtop], maxs[maxtop - 1] - 1, b[i] - b[maxs[maxtop]]);
                --maxtop;
            }
            while ( mintop && b[mins[mintop]] > b[i] )
            {
                Tree2.Insert(1, 1, n, mins[mintop], mins[mintop - 1] - 1, b[mins[mintop]] - b[i]);
                --mintop;
            }
            maxs[++maxtop] = i;
            mins[++mintop] = i;
            Data res = Tree2.Query(1, 1, n, i, maxs[0] - 1);
            if ( res.min_val == -i )
                ans -= res.count;
            if ( !b[i - 1] && b[maxs[1]] == i - 1 )
            {
                res = Tree2.Query(1, 1, n, maxs[1], maxs[0] - 1);
                if ( res.min_val == -i )
                    ans += res.count;
            }
        }
        else
        {
            maxtop = mintop = 0;
            maxs[0] = mins[0] = i;
        }
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2023-04-20 20:58  KafuuChinocpp  阅读(14)  评论(0)    收藏  举报