3.17 总结

前言:
废掉了


T1.线性代数

考场时想到了线性基,但是思路仅从基底入手,没想到如何维护这个基的最大值小于 \(n\)

题解:

容易想到线性代数,相当于就是求任意元素小于 \(n\) 的一维线性空间个数。

由于一个线性空间可能对应多个基,我们不妨以这个线性空间的最大值作为标识。

容易知道,每个基底在二进制表示下的最高位不同,所以按照二进制标识下最高位进行枚举。

\(dp[i][j][1/0]\) 表示考虑二进制表示下前 \(i\) 位,基大小为 \(j\),此时线性空间最大值是/否等于 \(n\) 在二进制表示下的前 \(i\) 位。

转移考虑是否要添加一个二进制标识下最高位为 \(i\) 的基底,或者前 \(j - 1\) 个第 \(i\) 位可以随便选,然后根据前 \(j - 1\) 个的结果选择第 \(j\) 个的第 \(i\) 为零或者 \(1\)(你不必知道前 \(j - 1\) 个异或出来的最大值第 \(i\) 位的数值,你只需要知道你的第 \(j\) 个数的第 \(i\) 位不自由即可,考试时我就卡在这里)。

参考代码

// ubsan: undefined
// accoders
#include <set>
#include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <random>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
//#define int long long
#define PII pair <int, int>
#define ULL unsigned long long
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); i++)
#define per(i,j,k) for (int i = (j); i >= (k); i--)

template <typename T>
void read (T &x) {
    x = 0; T f = 1; 
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... Arg) {
    read (x), read (Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write (T x) {
	if (x == 0) {
		putchar ('0');
		return;
	}
    bool flag = (x < 0 ? 1 : 0);
    x = (x < 0 ? -x : x);
    while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
    if (flag) putchar ('-');
    while (Poi_For_Print) putchar (Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write (T x, Args... Arg) {
    write (x); putchar (' '); write (Arg...);
}
template <typename T, typename... Args>
void print (T x, char ch) {
    write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }

const int Maxk = 30;
const LL Mod = 1e9 + 7;

int n;
int tp, st[Maxk + 5];

LL f[Maxk + 5][Maxk + 5];
LL dfs (int step, int cnt, int Limit) {
    if (step == -1) {
        return 1;
    }
    if (!Limit && f[step][cnt]) return f[step][cnt];
    int up = (Limit ? st[step] : 1);
    //在填第 cnt 个主元的第 step 位
    LL res = 0;
    rep (i, 0, up) {
        if (cnt == 0 && i == 1) continue;
        //根据前 cnt - 1 个主元异或结果的第 step 位决定第 cnt 个主元的第 step 位填什么
        res += dfs (step - 1, cnt, Limit & (i == up)) * (cnt == 0 ? 1 : (1 << (cnt - 1))) % Mod;
        res %= Mod;
    }
    //准备新加入主元,最高位为 step
    if (up)
        res += dfs (step - 1, cnt + 1, Limit), res %= Mod;
    if (!Limit) f[step][cnt] = res;
    return res;
}

signed main () {
    // freopen ("C:\\Users\\HP\\Desktop\\lihan\\1.in", "r", stdin);
    // freopen ("C:\\Users\\HP\\Desktop\\lihan\\1.out", "w", stdout);
    freopen ("algebra.in", "r", stdin);
    freopen ("algebra.out", "w", stdout);

    read (n);
    int tmp = n;
    while (tmp)
        st[tp++] = tmp & 1, tmp >>= 1;
    tp--;
    write (dfs (tp, 0, 1));
    return 0;
}

T2.

真秒,给我一辈子都想不出来

口胡

考虑一个 \(T_2\) 上一条路径在 \(T_1\) 上是否满足要求,条件是 \(T_2\) 上的点组成点集 \(S\),要求 \(T_2\)\(S\) 的导出子图点数 - 边数 = 1。

首先,对于一条路径 \(s, t\) 的点数是确定的,直接在 \(T_2\) 上求 \(Lca\) 就行了。

考虑 \(T_1\) 上一条边会提供什么贡献。

会将路径 \(s \rightarrow t (s \in S_1, t \in S_2 \cup S_3\) 的边数加 \(1\),发现 \(S_1, S_2, S_3\) 成树状,此时将 \(s \rightarrow t\) 抽象成点 \((dfn_s, dfn_t)\)\(dfn\) 表示 \(T_2\) 上的 \(dfs\) 序),然后就可以进行二维区间修改了。


T3.

应该是三道题中最简单的一道,当时没怎么看,被 T1 搞麻了。

转换一下题意,那么就是找到所有 \(S_{[l_i, r_i]} = S_{[l, r]}\) 的区间,然后在 \(1\) ~ \(n\) 中选最少的点,对于所有 \(i\),满足 \([l_i, r_i - 1]\) 里至少有一个选择的点。

容易想到贪心,从左往右扫描,选择未含选定点的区间的末尾。

预处理询问,要求询问 \(l, r\) 满足 \(r - l + 1 \leq \sqrt {n}\),做法就是枚举长度,然后扫一遍,用 \(map\) 将所有可能询问的字符串找到并求解。

\(r - l + 1 \leq \sqrt {n}\) 直接用预处理的回答

\(r - l + 1 \geq \sqrt {n}\) 容易发现需要加入的 \(*\) 不超过 \(\sqrt {n}\) 个,做法就是令 \(p = 1\),找到第一个左端点大于等于 \(p\) 的,然后令 \(p = r_i\)(相当于选择 \(r_i\))。寻找第一个左端点大于等于 \(p\) 的方法是利用 \(SA\) 数组,因为在按照后缀排序后,对于 \(S_{[i, n]}\),最长公共前缀一定是在 \(SA\) 中与 \(i\) 更近的点,在同侧的情况下,离的越远,公共前缀越短,那么可以按序将 \(SA\) 插入主席树中,找到 \(l, r\) 这个询问在 \(SA\) 上最长公共前缀 \(\geq R - L + 1\) 的区间 \([L, R]\),然后在版本 \([L, R]\) 中二分线段树找到第一个大于等于 \(p\) 的点。

参考代码

// ubsan: undefined
// accoders
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#pragma GCC optimize(2)
#include <set>
#include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <random>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
//#define int long long
#define PII pair <int, int>
#define ULL unsigned long long
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); i++)
#define per(i,j,k) for (int i = (j); i >= (k); i--)

template <typename T>
void read (T &x) {
    x = 0; T f = 1; 
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar ();
    }
    x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... Arg) {
    read (x), read (Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write (T x) {
	if (x == 0) {
		putchar ('0');
		return;
	}
    bool flag = (x < 0 ? 1 : 0);
    x = (x < 0 ? -x : x);
    while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
    if (flag) putchar ('-');
    while (Poi_For_Print) putchar (Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write (T x, Args... Arg) {
    write (x); putchar (' '); write (Arg...);
}
template <typename T, typename... Args>
void print (T x, char ch) {
    write (x); putchar (ch);
}
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }

const int Maxn = 1e5;
const int Maxk = 60;
const ULL P = 13331;
const int Inf = 0x3f3f3f3f;

int n, q, Size;
ULL pre[Maxn + 5];
char s[Maxn + 5];
map <ULL, int> ans;

LL w[Maxn + 5];
ULL Query (int l, int r) {
	return pre[r] - pre[l - 1] * w[r - l + 1];
}

int sa[Maxn + 5], id_bak[Maxn + 5];
//sa[i] len + 1 ~ 2 * len 排名为 i 的串的编号
//id_bak[i] 编号为 i 的串该被放进哪个桶 
int sort_id[Maxn + 5], poi_bak[Maxn + 5];
//sort_id[i] 按照 len + 1 ~ 2 * len 排序的排名为 i 的串的编号 
//poi_bak[i] 当前队列的最后一个元素的排名 
int tem[Maxn * 2 + 5]; 
void Init_Bak (int n, int m) {//基数排序,装桶 
	rep (i, 1, m)
		poi_bak[i] = 0;
	rep (i, 1, n)
		poi_bak[id_bak[i]]++;
	rep (i, 1, m)
		poi_bak[i] += poi_bak[i - 1];
}
void Sort_L (int n) {//按照 len + 1 ~ 2 * len 排序 
	per (i, n, 1)
		sa[poi_bak[id_bak[sort_id[i]]]--] = sort_id[i];
}
void Get_Sa (int n, int m, char *str) {
	rep (i, 1, n) {
		id_bak[i] = str[i] - 'a' + 1;
		sort_id[i] = i;
	}
	Init_Bak (n, m);
	Sort_L (n);
	
	for (int len = 1; len < n; len <<= 1) {
		Init_Bak (n, m);
		int Now_Rank = 0;
		rep (i, n - len + 1, n) {
			sort_id[++Now_Rank] = i;
		}
		rep (i, 1, n)
			if (sa[i] > len)
				sort_id[++Now_Rank] = sa[i] - len;
		Sort_L (n);
		
		rep (i, 1, n) tem[i] = id_bak[i];
		Now_Rank = 1; id_bak[sa[1]] = 1;
		rep (i, 2, n) {
			if (tem[sa[i - 1]] != tem[sa[i]] || tem[sa[i - 1] + len] != tem[sa[i] + len])
				Now_Rank++;
			id_bak[sa[i]] = Now_Rank;
		}
		m = Now_Rank;
	}
}
bool check (int x, int y, int len) {
    if (x + len - 1 > n) return 0;
    if (y + len - 1 > n) return 0;
	return Query (x, x + len - 1) == Query (y, y + len - 1);
}

#define ls(p) (Tr[p].ch[0])
#define rs(p) (Tr[p].ch[1])
#define tot(p) (Tr[p].tot)
#define mid ((l + r) >> 1)
struct Node {
	int tot;
    int ch[2];
};
struct Persistent_Tree {
	int cnt;
	int timestamp, rt[Maxn + 5];
    Node Tr[Maxn * Maxk + 5];
    
	void Push_Up (int p) {
		tot (p) = tot (ls (p)) + tot (rs (p));
	}
    int New_Node (int Root) {
        int p = ++cnt;
        Tr[p] = Tr[Root];
        return p;
    }
    int Update (int Root, int Index, int l, int r) {
        int p = New_Node (Root);
        if (l == r) {
            tot (p) ++;
            return p;
        }
        if (Index <= mid) ls (p) = Update (ls (p), Index, l, mid);
        else rs (p) = Update (rs (p), Index, mid + 1, r);
    	Push_Up (p);
        return p;
    }
	void Update (int Index) {
        timestamp++;
		rt[timestamp] = Update (rt[timestamp - 1], Index, 1, n);
	}
    int Query (int p, int q, int ql, int qr, int l, int r) {
        if (ql > qr) return -1;
        if (tot (q) - tot (p) == 0) return -1;
        if (l == r) return l;
        int res = -1;
        if (ql <= mid) res = Query (ls (p), ls (q), ql, qr, l, mid);
        if (res != -1) return res;
        if (qr > mid) res = Query (rs (p), rs (q), ql, qr, mid + 1, r);
        return res;
    }
}Tree;

signed main () {
    // freopen ("C:\\Users\\HP\\Desktop\\lihan\\1.in", "r", stdin);
    // freopen ("C:\\Users\\HP\\Desktop\\lihan\\1.out", "w", stdout);
    freopen ("string.in", "r", stdin);
    freopen ("string.out", "w", stdout);
	
	w[0] = 1; rep (i, 1, Maxn) w[i] = w[i - 1] * P;
	read (n, q);
	Size = 10;
	scanf ("%s", s + 1);
	rep (i, 1, n) {
		pre[i] = pre[i - 1] * P + s[i] - 'a' + 1;
	}
	rep (len, 1, Size) {
		map <ULL, PII> bak;
		rep (l, 1, n - len + 1) {
			int r = l + len - 1;
            ULL tmp = Query (l, r);
			if (bak.find (tmp) != bak.end ()) {
                if (bak[tmp].se <= l) {
                    ans[tmp] = ans[tmp] + 1;
                    bak[tmp] = MP (l, r);
                }
            }
			else {
				ans[tmp] = 1;
			    bak[tmp] = MP (l, r);
            }
		}
	}
	
	Get_Sa (n, 26, s); 
	
	rep (i, 1, n)
        sort_id[sa[i]] = i;
	
    rep (i, 1, n) {
		Tree.Update (sa[i]); 
    }
	
	rep (i, 1, q) {
		int L, R; read (L, R);
        if (L == R) {
            print (-1, '\n');
            continue;
        }
		if (R - L + 1 <= Size) {
			print (ans[Query (L, R)], '\n');
			continue;
		}
		else {
            int res = 0;
			int ql, qr;
			int l = 0, r = sort_id[L];
			while (l + 1 < r) {
				if (check (sa[mid], L, R - L + 1)) r = mid;
				else l = mid; 
			}
			ql = r;
			l = sort_id[L], r = n + 1;
			while (l + 1 < r) {
				if (check (sa[mid], L, R - L + 1)) l = mid;
				else r = mid;
			}
			qr = l;
            int p = 1;
			while (Tree.Query (Tree.rt[ql - 1], Tree.rt[qr], p, n, 1, n) != -1) {
                res++;
                p = Tree.Query (Tree.rt[ql - 1], Tree.rt[qr], p, n, 1, n) + (R - L);
            }
            print (res, '\n');
		}
	}
    return 0;
}
posted @ 2023-03-17 16:25  C2022lihan  阅读(22)  评论(0编辑  收藏  举报