首页 > 其他分享 >2023省选武汉联测6

2023省选武汉联测6

时间:2023-04-20 21:14:22浏览次数:42  
标签:return val 省选 sum max1 int 联测 2023 now

T1 线性代数

实际上我们需要求解值域 \(\le n\) 的线性空间的个数,考虑将线性空间与线性基一一对应,为了使得一个线性基唯一对应一个线性空间,我们将主元列上的非主元全部消成 \(0\) ,发现此时将线性基全部异或得到的值为原集合的最大值,并且可以做到一一对应。(化简为最简阶梯形矩阵)

于是考虑进行 dp ,设 \(f_{i,j,k=0/1}\) 表示当前考虑到了第 \(i\) 位,总共有 \(j\) 个主元,是否卡到了上界的方案数,枚举第 \(i\) 位为 \(c\) ,考虑第 \(i\) 为是否为主元,如果为主元,则 \(f_{i+1,j,k}\to f_{i,j+1,[k\operatorname{and}c=bit_i]}\) ,如果不是主元,由于最大值是将所有数进行异或,假设当前第 \(i\) 位异或的结果为 \(c\) ,当 \(c\) 为 \(1\) 时,发现 \(\sum_{k=0}^{\infty}\binom{j}{2k}=2^{j-1}\) ,当 \(c\) 为 \(0\) 时,发现 \(\sum_{k=0}^{\infty}\binom{j}{2k+1}=2^{j-1}\) ,因此有转移 \(f_{i+1,j,k}\times 2^{j-1}\to f_{i,j,[k\operatorname{and}c=bit_i]}\) 。

code

#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 45;
const int mod = 1e9 + 7;
int n, f[max1 + 5][max1 + 5][2], ans;
void Add ( int &x, int y )
{
	x = x + y;
	if ( x >= mod )
		x = x - mod;
	return;
}
int main ()
{
	freopen("algebra.in", "r", stdin);
	freopen("algebra.out", "w", stdout);
	scanf("%d", &n);
	f[30][0][1] = 1;
	for ( int i = 29; i >= 0; i -- )
	{
		for ( int j = 0; j <= 30; j ++ )
		{
			for ( int k = 0; k < 2; k ++ )
			{
				int m = k ? ( n >> i & 1 ) : 1;
				for ( int c = 0; c <= m; c ++ )
				{
					if ( c )
						Add(f[i][j + 1][k && c == m], f[i + 1][j][k]);
					if ( j > 0 || !c )
						Add(f[i][j][k && c == m], ( 1LL << max(j - 1, 0) ) * f[i + 1][j][k] % mod);
				}
			}
		}
	}
	for ( int j = 0; j <= 30; j ++ )
		for ( int k = 0; k < 2; k ++ )
			Add(ans, f[0][j][k]);
	printf("%d\n", ans);
	return 0;
}

T2 森林

容易发现联通块很难进行枚举,因此考虑枚举一条链,比较显然存在一种 \(O(n^2)\) 的做法:枚举根节点 \(i\) ,从 \(i\) 开始 Dfs ,考虑判断链 \(i\to u\) 上的点在第一棵树上是一个联通块,只需要判断这些点之间的连边个数 \(sum\) 等于链长 \(len\) 减一即可。

考虑优化枚举的过程,一个比较套路的想法是用点分治优化链的枚举,假设当前的分治重心为 \(x\) ,我们只考虑长度大于 \(1\) 的链,首先考虑查询,假设当前递归到的节点为 \(v\) ,假设之前扫过的点组成的集合为 \(S\) ,那么我们需要求解 \(\sum_{u\in S}f(u,v)\) ,其中当 \(u\to v\) 的链在第一棵树中为一个联通块时, \(f\) 函数返回 \(1\) ,否则返回 \(0\) 。发现对于一条链,链长减边数的最小值为 \(1\) ,也就是链长减边数取到最小值时合法,因此考虑用线段树维护区间最小值以及最小值的个数,由于 \(v\) 可以进行枚举,因此考虑在线段树第 \(u\) 个叶节点维护 \(f(u,v)\) 的值,假设当前我们已将加入了 \(father_v\) 到 \(x\) 的链所造成的贡献,考虑连接 \(v\) 所造成的影响,枚举第 \(1\) 棵树中与 \(v\) 相连的点 \(a\) ,如果 \(a\in S\) ,显然此时造成的影响是对 \(a\) 子树内所有点区间加,如果 \(a\) 位于 \(x\to v\) 的链上,那么会造成全局加的影响,如果是其他情况,那么没有贡献,很容易用线段树维护上述操作。

code

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int max1 = 1e5;
const int inf = 0x3f3f3f3f;
int T, n;
long long ans;
vector <int> edge[2][max1 + 5];
int siz[max1 + 5], root;
bool vis[max1 + 5], flag[max1 + 5], is_ancestor[max1 + 5];
int in[max1 + 5], out[max1 + 5], dfs_clock;
int val[max1 + 5];
struct Data
{
    int min_val, cnt;
    Data () {}
    Data ( int __min_val, int __cnt )
        { min_val = __min_val, cnt = __cnt; }
    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, cnt + A.cnt);
    }
};
struct Segment_Tree
{
    #define lson(now) ( now << 1 )
    #define rson(now) ( now << 1 | 1 )
    Data tree[max1 * 4 + 5];
    int tag[max1 * 4 + 5];
    void Push_Down ( int now )
    {
        if ( tag[now] )
        {
            tree[lson(now)].min_val += tag[now];
            tree[rson(now)].min_val += tag[now];
            tag[lson(now)] += tag[now];
            tag[rson(now)] += tag[now];
            tag[now] = 0;
        }
        return;
    }
    void Build ( int now, int l, int r )
    {
        tag[now] = 0;
        if ( l == r )
            { tree[now] = Data(inf, 1); return; }
        int mid = l + r >> 1;
        Build(lson(now), l, mid);
        Build(rson(now), mid + 1, r);
        tree[now] = tree[lson(now)] + tree[rson(now)];
        return;
    }
    void Insert ( int now, int l, int r, int pos, const Data &d )
    {
        if ( l == r )
            { tree[now] = d; return; }
        Push_Down(now);
        int mid = l + r >> 1;
        if ( pos <= mid )
            Insert(lson(now), l, mid, pos, d);
        else
            Insert(rson(now), mid + 1, r, pos, d);
        tree[now] = tree[lson(now)] + tree[rson(now)];
        return;
    }
    void Update ( int now, int l, int r, int ql, int qr, int x )
    {
        if ( l >= ql && r <= qr )
        {
            tree[now].min_val += x;
            tag[now] += x;
            return;
        }
        Push_Down(now);
        int mid = l + r >> 1;
        if ( ql <= mid )
            Update(lson(now), l, mid, ql, qr, x);
        if ( qr > mid )
            Update(rson(now), mid + 1, r, ql, qr, x);
        tree[now] = tree[lson(now)] + tree[rson(now)];
        return;
    }
}Tree;
void Get_Size ( int now, int fa )
{
    siz[now] = 1;
    for ( auto v : edge[1][now] )
    {
        if ( v == fa || vis[v] )
            continue;
        Get_Size(v, now);
        siz[now] += siz[v];
    }
    return;
}
void Get_Root ( int now, int fa, int sum )
{
    bool is_root = true;
    for ( auto v : edge[1][now] )
    {
        if ( v == fa || vis[v] )
            continue;
        Get_Root(v, now, sum);
        if ( siz[v] > sum >> 1 )
            is_root = false;
    }
    if ( sum - siz[now] > sum >> 1 )
        is_root = false;
    if ( is_root )
        root = now;
    return;
}
void Clear ( int now, int fa )
{
    in[now] = out[now] = 0;
    flag[now] = true;
    for ( auto v : edge[1][now] )
    {
        if ( v == fa || vis[v] )
            continue;
        Clear(v, now);
    }
    return;
}
void End_Flag ( int now, int fa )
{
    flag[now] = false;
    for ( auto v : edge[1][now] )
    {
        if ( v == fa || vis[v] )
            continue;
        End_Flag(v, now);
    }
    return;
}
void Query ( int now, int fa, int depth, int sum, int pre )
{
    for ( auto v : edge[0][now] )
    {
        if ( flag[v] )
        {
            if ( in[v] )
                Tree.Update(1, 1, sum, in[v], out[v], -1);
            else if ( is_ancestor[v] )
                Tree.Update(1, 1, sum, 1, pre, -1);
        }
    }
    is_ancestor[now] = true;
    Data d = Tree.tree[1];
    if ( d.min_val == 1 - depth )
        ans += d.cnt;
    for ( auto v : edge[1][now] )
    {
        if ( v == fa || vis[v] )
            continue;
        Query(v, now, depth + 1, sum, pre);
    }
    is_ancestor[now] = false;
    for ( auto v : edge[0][now] )
    {
        if ( flag[v] )
        {
            if ( in[v] )
                Tree.Update(1, 1, sum, in[v], out[v], 1);
            else if ( is_ancestor[v] )
                Tree.Update(1, 1, sum, 1, pre, 1);
        }
    }
    return;
}
void Update ( int now, int fa, int depth, int sum )
{
    in[now] = ++dfs_clock;
    val[now] = val[fa];
    for ( auto v : edge[0][now] )
        if ( is_ancestor[v] )
            ++val[now];
    is_ancestor[now] = true;
    Tree.Insert(1, 1, sum, in[now], Data(depth - val[now], 1));
    for ( auto v : edge[1][now] )
    {
        if ( v == fa || vis[v] )
            continue;
        Update(v, now, depth + 1, sum);
    }
    out[now] = dfs_clock;
    is_ancestor[now] = false;
    return;
}
void Dfs ( int now, int sum )
{
    Clear(now, 0);
    dfs_clock = 0;
    Tree.Build(1, 1, sum);
    in[now] = out[now] = ++dfs_clock;
    val[now] = 0;
    is_ancestor[now] = true;
    Tree.Insert(1, 1, sum, in[now], Data(1 - val[now], 1));
    for ( auto v : edge[1][now] )
    {
        if ( vis[v] )
            continue;
        Query(v, now, 1, sum, out[now]);
        Update(v, now, 2, sum);
        out[now] = dfs_clock;
    }
    End_Flag(now, 0);
    is_ancestor[now] = false;
    vis[now] = true;
    for ( auto v : edge[1][now] )
    {
        if ( vis[v] )
            continue;
        Get_Size(v, now);
        Get_Root(v, now, siz[v]);
        Dfs(root, siz[v]);
    }
    return;
}
void Work ()
{
    scanf("%d", &n);
    for ( int i = 1; i <= n; i ++ )
        edge[0][i].clear(), edge[1][i].clear();
    for ( int i = 1, u, v; i <= n - 1; i ++ )
    {
        scanf("%d%d", &u, &v);
        edge[0][u].push_back(v);
        edge[0][v].push_back(u);
    }
    for ( int i = 1, u, v; i <= n - 1; i ++ )
    {
        scanf("%d%d", &u, &v);
        edge[1][u].push_back(v);
        edge[1][v].push_back(u);
    }
    ans = 0;
    for ( int i = 1; i <= n; i ++ )
        vis[i] = is_ancestor[i] = flag[i] = false;
    Get_Size(1, 0);
    Get_Root(1, 0, siz[1]);
    Dfs(root, siz[1]);
    ans += n;
    printf("%lld\n", ans);
    return;
}
int main ()
{
    freopen("forest.in", "r", stdin);
    freopen("forest.out", "w", stdout);
    scanf("%d", &T);
    while ( T -- )
        Work();
    return 0;
}

T3 字符串

发现题解做法需要用 SAM ,考虑抛弃题解。

考虑用 SA 代替 SAM ,发现题解中 SAM 的主要作用是维护有序的 endpos 集合,设当前询问的区间为 \([l,r]\) ,由于与 \(s(l,n)\) 匹配长度大于 \(r-l+1\) 的后缀在 SA 上表现为一段连续的区间,因此可以用 SA + ST 表找到这段区间,此时我们只需要得到有序的 endpos 集合,显然可以用主席树进行维护。

首先考虑暴力的做法,假设得到询问串所匹配的 endpos 集合为 \(S\) ,如果在位置 \(x\) 之前插入 * ,那么所有包含 \(x-1\) 的区间都会无法匹配,因此设上一次在第 \(k\) 个位置前插入 * ,找到第一个区间左端点大于等于 \(k\) 的区间,此时贪心的在第 \(r\) 个位置之前插入 * 最优。

很容易发现这个过程可以用主席树优化,发现记忆化后复杂度正确,简单证明一下,我们取阀值 \(B=\sqrt{n}\) ,对于 \(len\le B\) 的询问,发现最多扫过 \(n\sqrt{n}\) 个区间,复杂度为 \(O(n\sqrt{n}\log n)\) ,对于 \(len> B\) 的询问,发现 \(res\le B\) ,因此复杂度为 \(O(q\sqrt{n}\log n)\) 。

code

#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
#include <iostream>
using namespace std;
const int max1 = 1e5;
const int inf = 0x3f3f3f3f;
int n, q;
char s[max1 + 5];
int lim, x[max1 + 5], y[max1 + 5], bin[max1 + 5];
int sa[max1 + 5], rk[max1 + 5], height[max1 + 5];
map < pair <int, int>, int > Map;
void Suffix_Array ()
{
    lim = 26;
    for ( int i = 1; i <= n; i ++ )
        x[i] = s[i] - 'a' + 1;
    for ( int i = 1; i <= n; i ++ )
        ++bin[x[i]];
    for ( int i = 1; i <= lim; i ++ )
        bin[i] += bin[i - 1];
    for ( int i = n; i >= 1; i -- )
        sa[bin[x[i]]--] = i;
    for ( int w = 1; w <= n; w <<= 1 )
    {
        int num = 0;
        for ( int i = n - w + 1; i <= n; i ++ )
            y[++num] = i;
        for ( int i = 1; i <= n; i ++ )
            if ( sa[i] > w )
                y[++num] = sa[i] - w;
        for ( int i = 1; i <= lim; i ++ )
            bin[i] = 0;
        for ( int i = 1; i <= n; i ++ )
            ++bin[x[i]];
        for ( int i = 1; i <= lim; i ++ )
            bin[i] += bin[i - 1];
        for ( int i = n; i >= 1; i -- )
            sa[bin[x[y[i]]]--] = y[i];
        for ( int i = 1; i <= n; i ++ )
            y[i] = x[i];
        num = 1, x[sa[1]] = 1;
        for ( int i = 2; i <= n; i ++ )
        {
            if ( y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + w] == y[sa[i] + w] )
                x[sa[i]] = num;
            else
                x[sa[i]] = ++num;
        }
        lim = num;
        if ( lim == n )
            break;
    }
    for ( int i = 1; i <= n; i ++ )
        rk[sa[i]] = i;
    for ( int i = 1; i <= n; i ++ )
    {
        if ( rk[i] == 1 )
            continue;
        int k = height[rk[i - 1]];
        if ( k )
            --k;
        while ( i + k <= n && sa[rk[i] - 1] + k <= n && s[i + k] == s[sa[rk[i] - 1] + k] )
            ++k;
        height[rk[i]] = k;
    }
    return;
}
struct ST_List
{
    int list[max1 + 5][20];
    void Pre_Work ()
    {
        int now = 0;
        for ( int i = 1; i <= n; i ++ )
            list[i][0] = height[i];
        for ( int j = 1; j < 20; j ++ )
            for ( int i = 1; i + ( 1 << j ) - 1 <= n; i ++ )
                list[i][j] = min(list[i][j - 1], list[i + ( 1 << j - 1 )][j - 1]);
        return;
    }
    int Query_Pre ( int pos, int k )
    {
        for ( int i = 19; i >= 0; i -- )
            if ( pos - ( 1 << i ) + 1 >= 1 && list[pos - ( 1 << i ) + 1][i] >= k )
                pos -= 1 << i;
        return pos;
    }
    int Query_Next ( int pos, int k )
    {
        ++pos;
        for ( int i = 19; i >= 0; i -- )
            if ( pos + ( 1 << i ) - 1 <= n && list[pos][i] >= k )
                pos += 1 << i;
        return pos - 1;
    }
}ST;
struct President_Tree
{
    #define lson(now) tree[now].son[0]
    #define rson(now) tree[now].son[1]
    #define sum(now) tree[now].sum
    struct Struct_President_Tree
        { int son[2], sum; } tree[max1 * 40 + 5];
    int root[max1 + 5], total;
    void Clear ()
        { root[0] = total = lson(0) = rson(0) = sum(0) = 0; return; }
    int Insert ( int pre, int l, int r, int pos )
    {
        int now = ++total;
        tree[now] = tree[pre];
        ++sum(now);
        if ( l == r )
            return now;
        int mid = l + r >> 1;
        if ( pos <= mid )
            lson(now) = Insert(lson(pre), l, mid, pos);
        else
            rson(now) = Insert(rson(pre), mid + 1, r, pos);
        return now;
    }
    void Insert ( int pos, int val )
        { root[pos] = Insert(root[pos - 1], 1, n, val); return; }
    int Query ( int L, int R, int l, int r, int pos )
    {
        if ( !( sum(R) - sum(L) ) )
            return inf;
        if ( l == r )
            return l;
        int mid = l + r >> 1, res = inf;
        if ( pos <= mid )
            res = Query(lson(L), lson(R), l, mid, pos);
        if ( res == inf )
            res = Query(rson(L), rson(R), mid + 1, r, pos);
        return res;
    }
    int Query ( int L, int R, int pos )
        { return Query(root[L - 1], root[R], 1, n, pos); }
}Tree;
void Solve ()
{
    int l, r;
    scanf("%d%d", &l, &r);
    if ( l == r )
        printf("-1\n");
    else
    {
        int L = ST.Query_Pre(rk[l], r - l + 1), R = ST.Query_Next(rk[l], r - l + 1);
        if ( Map.find(make_pair(L, r - l + 1)) != Map.end() )
            printf("%d\n", Map[make_pair(L, r - l + 1)]);
        else
        {
            int k = 0, res = 0;
            while ( k < inf )
            {
                k = Tree.Query(L, R, k) + r - l;
                ++res;
            }
            Map[make_pair(L, r - l + 1)] = res - 1;
            printf("%d\n", res - 1);
        }
    }
    return;
}
int main ()
{
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    scanf("%d%d%s", &n, &q, s + 1);
    Suffix_Array();
    cerr << "Suffix" << endl;
    ST.Pre_Work();
    cerr << "ST" << endl;
    Tree.Clear();
    for ( int i = 1; i <= n; i ++ )
        Tree.Insert(i, sa[i]);
    cerr << "Tree" << endl;
    while ( q -- )
        Solve();
    return 0;
}

标签:return,val,省选,sum,max1,int,联测,2023,now
From: https://www.cnblogs.com/KafuuChinocpp/p/17338335.html

相关文章

  • 2023省选武汉联测10
    T1矩阵随机一个向量\(V\),判断\(V\timesA\timesB\)是否等于\(V\timesC\)即可,实质上我们在判断对于每个\(i\in[1,n]\)\(\sum_{k=1}^nV_k\sum_{p=1}^{n}A_{k,p}B_{p,i}\)是否等于\(\sum_{k=1}^{n}V_kC_{k,i}\)。code#include<cstdio>#include<vector>#incl......
  • 2023省选武汉联测9
    T1背包问题模板比较套路的,我们考虑进行二进制拆分,对于数量\(A\),我们首先从小到大拆分为\(1,2,4...2^k\),对于剩余的\(w\),我们直接按照它的二进制位拆分即可,这样问题转化为比较简单的\(0/1\)背包。由于\(b_i\)的范围很小,如果将物体体积用二进制数表示,发现二进制上为\(......
  • 2023省选武汉联测13
    T1构树直接\(O(n^2)\)暴力枚举连边即可。code#include<cstdio>#include<vector>#include<set>#include<utility>usingnamespacestd;constintmax1=1000;intn,Min[max1+5],Max[max1+5];vector<int>part[max1+5];intcolo......
  • # 2023省选武汉联测12
    T1图案首先是题解做法:考虑对于每个\(r\),判断\(s[1,r]\)是否为一个图案,设\(r=ik+j\),其中\(0\lej\lei\),如果存在一组这样的\((i,j)\)满足\(s[1,r-i]=s[i+1,r]\),那么\(s[1,r]\)是一个图案,考虑这样做的正确性,如果\(s[1,r-i]=s[i+1,r]\),那么一定有\(s[i+1,r-2i]=s......
  • 2023省选武汉联测11
    T1游戏对于树上三点\((u,v,w)\),一定存在一个点\(p\)满足\(p\tou\)与\(p\tov\)与\(p\tow\)的路径两两不重合,考虑枚举\(p\)计算答案,由于题目给定\(\operatorname{dis}(u,v),\operatorname{dis}(u,w),\operatorname{dis}(v,w)\),因此我们首先用解方程的方法求解\(......
  • 2023-04-20:有一堆石头,用整数数组 stones 表示 其中 stones[i] 表示第 i 块石头的重量
    2023-04-20:有一堆石头,用整数数组stones表示其中stones[i]表示第i块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎假设石头的重量分别为x和y,且x<=y那么粉碎的可能结果如下:如果x==y,那么两块石头都会被完全粉碎;如果x!=y,那么重量为x的石头将......
  • 西南民族大学 2023 天梯自主训练 4
    西南民族大学2023天梯自主训练4 多项式A除以B思路:a表示被除多项式,b表示除数多项式,一次运算的商的项p为a的最高项-b的最高项,商的系数q为a的最高项的系数除以b的最高项的系数,将所有a中项数和b的项数乘p后相同的系数减去b项的系数乘q(...haoluan...看图吧) #include<bits/s......
  • 2023/4/20 SCRUM个人博客
    1、我昨天的成就:1111111111111昨天完成了识别视频画面中的人脸并且绘制人脸的边框到视频画面上面2、遇到了什么困难困难在于opencv、dlib等的使用,查阅了相关资料以及视频后得以解决3、今天的任务今天的任务主要是完成人脸课堂集中度检测画面的绘制,以从暗到亮表......
  • 每日总结2023-04-20
    今天完成了对于界面的初步优化,但对于基于通过dialog对话框的跳转的传值不理解,无法将主界面的信息通过dialog传递到另一个页面。 初步完成的页面: 剩余任务还有Android的网络获取定位输出详细地址。 ......
  • 2023/4/20
    今日战立会议,主要进行数据库的规划,建表进行下一步的实现。每个人分析自己的数据需求,进行建表。进一步实现每个人的任务。 ......