NOI2018笔试改错
LOJ513 「LibreOJ NOI Round #1」&# …
May all the beauty be blessed.
题目地址:洛谷:【P4219】[BJOI2014]大融合 – 洛谷、BZOJ:Problem 4530. — [Bjoi2014]大融合
小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。
输入格式:
第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
接下来的Q行,每行是如下两种格式之一:
A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
1≤N,Q≤100000
输出格式:
对每个查询操作,输出被查询的边的负载。
输入样例#1:
8 6 A 2 3 A 3 4 A 3 8 A 8 7 A 6 5 Q 3 8
输出样例#1:
6
子树信息LCT的写法,对于每个点记录一个虚子树的大小,然后在access和link的时候维护一下轻重交替的信息就好。
复杂度$O(n \log n)$。
// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1; register char c = fgc();
for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
return res * neg;
}
inline char readsingle() {
char c;
while(!isalpha(c = fgc())) {}
return c;
}
const int MAXN = 100005;
struct Node {
int ch[2], fa, tsiz, siz;
bool rev;
} tr[MAXN];
inline void update(int p) {
tr[p].siz = tr[tr[p].ch[0]].siz + tr[tr[p].ch[1]].siz + tr[p].tsiz + 1;
}
inline bool isleft(int p) {
return tr[tr[p].fa].ch[0] == p;
}
inline bool isroot(int p) {
return tr[tr[p].fa].ch[0] != p && tr[tr[p].fa].ch[1] != p;
}
inline void reverse(int p) {
tr[p].rev ^= 1;
std::swap(tr[p].ch[0], tr[p].ch[1]);
}
inline void pushdown(int p) {
if(tr[p].rev) {
if(tr[p].ch[0]) reverse(tr[p].ch[0]);
if(tr[p].ch[1]) reverse(tr[p].ch[1]);
tr[p].rev ^= 1;
}
}
int sta[MAXN], stop;
inline void pushto(int p) {
stop = 0;
while(!isroot(p)) {
sta[stop++] = p; p = tr[p].fa;
}
sta[stop++] = p;
while(stop) {
pushdown(sta[--stop]);
}
}
inline void rotate(int p) {
bool t = !isleft(p); int fa = tr[p].fa, ffa = tr[fa].fa;
tr[p].fa = ffa; if(!isroot(fa)) tr[ffa].ch[!isleft(fa)] = p;
tr[fa].ch[t] = tr[p].ch[!t]; tr[tr[fa].ch[t]].fa = fa;
tr[p].ch[!t] = fa; tr[fa].fa = p;
update(fa);
}
inline void splay(int p) {
pushto(p);
for(int fa = tr[p].fa; !isroot(p); rotate(p), fa = tr[p].fa) {
if(!isroot(fa)) rotate(isleft(fa) == isleft(p) ? fa : p);
}
update(p);
}
inline void access(int p) {
for(int q = 0; p; q = p, p = tr[p].fa) {
splay(p);
tr[p].tsiz += tr[tr[p].ch[1]].siz;
tr[p].tsiz -= tr[q].siz;
tr[p].ch[1] = q; update(p);
}
}
inline void makert(int p) {
access(p);
splay(p);
reverse(p);
}
inline int findrt(int p) {
access(p); splay(p);
while(tr[p].ch[0]) p = tr[p].ch[0];
return p;
}
inline void split(int u, int v) {
makert(u);
access(v);
splay(v);
}
inline void link(int u, int v) {
split(u, v); tr[u].fa = v; tr[v].tsiz += tr[u].siz; update(v);
}
inline void cut(int u, int v) {
split(u, v);
if(tr[v].ch[0] != u || tr[u].ch[1]) return;
tr[v].ch[0] = tr[u].fa = 0;
}
int n, q;
int main() {
n = readint(); q = readint();
while(q--) {
char op = readsingle();
int x = readint(), y = readint();
if(op == 'A') {
link(x, y);
} else {
split(x, y);
printf("%d\n", tr[x].siz * (tr[y].siz - tr[x].siz));
}
}
return 0;
}
题目地址:洛谷:【P2480】[SDOI2010]古代猪文 – 洛谷、BZOJ:Problem 1951. — [Sdoi2010]古代猪文
猪王国的文明源远流长,博大精深。
iPig在大肥猪学校图书馆中查阅资料,得知远古时期猪文文字总个数为N。当然,一种语言如果字数很多,字典也相应会很大。当时的猪王国国王考虑到如果修一本字典,规模有可能远远超过康熙字典,花费的猪力、物力将难以估量。故考虑再三没有进行这一项劳猪伤财之举。当然,猪王国的文字后来随着历史变迁逐渐进行了简化,去掉了一些不常用的字。
iPig打算研究古时某个朝代的猪文文字。根据相关文献记载,那个朝代流传的猪文文字恰好为远古时期的k分之一,其中k是N的一个正约数(可以是1和N)。不过具体是哪k分之一,以及k是多少,由于历史过于久远,已经无从考证了。
iPig觉得只要符合文献,每一种能整除N的k都是有可能的。他打算考虑到所有可能的k。显然当k等于某个定值时,该朝的猪文文字个数为N / k。然而从N个文字中保留下N / k个的情况也是相当多的。iPig预计,如果所有可能的k的所有情况数加起来为P的话,那么他研究古代文字的代价将会是G的P次方。
现在他想知道猪王国研究古代文字的代价是多少。由于iPig觉得这个数字可能是天文数字,所以你只需要告诉他答案除以999911659的余数就可以了。
计算$G^{\sum_{d|N} \mathrm{C}^d_N} \bmod 999911659$。
输入格式:
输入文件ancient.in有且仅有一行:两个数N、G,用一个空格分开。
输出格式:
输出文件ancient.out有且仅有一行:一个数,表示答案除以999911659的余数。
输入样例#1:
4 2
输出样例#1:
2048
数据规模
10%的数据中,1 <= N <= 50;
20%的数据中,1 <= N <= 1000;
40%的数据中,1 <= N <= 100000;
100%的数据中,1 <= G <= 1000000000,1 <= N <= 1000000000。
首先,组合数一堆加起来幂次可能很大,这时我们可以应用欧拉降幂公式降低幂次。我们把答案变成了这样子
$$ G^{\sum_{d|N} \mathrm{C}^d_N \bmod \varphi(999911659) + \varphi(999911659)} $$
由于$999911659$是个质数,我们知道$\varphi(999911659)=999911658$,而这个数并不是质数,并不好办。我们考虑使用扩展Lucas定理,将$999911658$分解质因数为$2 \times 3 \times 4679 \times 35617$,分别对每个质因数求一次组合数,然后应用中国剩余定理解这个同余方程组,得到最终结果。
复杂度$O(\sqrt{n} \log n)$。
// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
register LL res = 0, neg = 1; register char c = fgc();
for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
return res * neg;
}
inline LL fpow(LL n, LL k, LL p) {
LL res = 1;
for(; k; k >>= 1) {
if(k & 1) res = res * n % p;
n = n * n % p;
}
return res;
}
const int MAXN = 100005, MO[] = {0, 2, 3, 4679, 35617, 999911659};
LL N, G, a[5], mul[MAXN], inv[MAXN];
inline void init(LL p) {
mul[0] = mul[1] = inv[0] = inv[1] = 1;
for(int i = 2; i < p; i++) {
mul[i] = mul[i - 1] * i % p;
}
for(int i = 2; i < p; i++) {
inv[i] = (-(p / i) * inv[p % i] % p + p) % p;
}
for(int i = 2; i < p; i++) {
inv[i] = inv[i - 1] * inv[i] % p;
}
}
LL C(LL n, LL m, LL p) {
if(m > n) return 0;
if(m == 0) return 1;
if(n < p && m < p) return mul[n] * inv[m] * inv[n - m] % p;
return C(n % p, m % p, p) * C(n / p, m / p, p) % p;
}
inline LL calSum(LL p) {
init(p);
LL res = 0;
for(int i = 1; i * i <= N; i++) {
if(N % i) continue;
res = (res + C(N, i, p)) % p;
if(i * i == N) continue;
res = (res + C(N, N / i, p)) % p;
}
return res;
}
inline LL crt() {
LL p = 1, res = 0;
for(int i = 1; i <= 4; i++) {
p *= MO[i];
}
for(int i = 1; i <= 4; i++) {
LL m = p / MO[i];
res = (res + a[i] * m * fpow(m, MO[i] - 2, MO[i])) % p;
}
return res;
}
int main() {
N = readint(); G = readint();
if(G % MO[5] == 0) {
puts("0"); return 0;
}
for(int i = 1; i <= 4; i++) {
a[i] = calSum(MO[i]);
}
printf("%lld", fpow(G, crt() + MO[5] - 1, MO[5]));
return 0;
}
题目地址:洛谷:【P2414】[NOI2011]阿狸的打字机 – 洛谷、BZOJ:Problem 2434. — [Noi2011]阿狸的打字机
阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。
打字机上只有28个按键,分别印有26个小写英文字母和’B’、’P’两个字母。经阿狸研究发现,这个打字机是这样工作的:
例如,阿狸输入aPaPBbP,纸上被打印的字符如下:
a aa ab
我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。
阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
你有一个打字机,打字机支持三种操作
纸上会被印上若干行文字,现在你有$m$组询问,每组询问求第$x$行的字符串在第$y$行的字符串中出现了几次。
输入格式:
输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。
第二行包含一个整数m,表示询问个数。
接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。
输出格式:
输出m行,其中第i行包含一个整数,表示第i个询问的答案。
输入样例#1:
aPaPBbP 3 1 2 1 3 2 3
输出样例#1:
2 1 0
数据范围:
对于100%的数据,n<=100000,m<=100000,第一行总长度<=100000。
首先我们可以对所有串建一个AC自动机,由于上面的操作的一些特殊性质,其实可以一位一位地边操作边插入到Trie树中。
处理每个询问的时候都在AC自动机上跑匹配不现实,会TLE。我们需要一种优化到$O(\log n)$回答单次询问的方法。我们想到了fail树,问$y$中有几个$x$等价于问fail树中$x$的末结点子树中有几个属于$y$路径上的节点。
我们考虑把fail树建出来,处理DFS序,对DFS序建一个树状数组。我们还是按照读入操作的那个顺序去遍历这个Trie树,每到一个节点对树状数组中这个节点DFS序的位置+1,退出一个节点就-1,这样,当到达每个串的结尾节点时,它的Trie树上的树链节点都被+1了,此时对于这个串作为$y$的询问就可以回答了,直接在树状数组上查$x$的子树和就好。
因此,思路是,离线处理这些询问,把询问按$y$分类,每遍历到一个字符串结尾的时候回答该串作为$y$的询问,这样复杂度就是很靠谱的$O(n \log n)$了。
// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
const int MAXN = 100005;
char op[MAXN];
int n, m, ans[MAXN];
typedef std::pair<int, int> PII;
std::vector<PII> vec[MAXN];
int ch[MAXN][26], fa[MAXN], fail[MAXN], end[MAXN], tot, stot;
std::vector<int> gra[MAXN];
inline void insert() {
int p = 0;
for(int i = 1; i <= n; i++) {
if(op[i] == 'P') {
end[++stot] = p;
} else if(op[i] == 'B') {
p = fa[p];
} else {
int c = op[i] - 'a';
if(!ch[p][c]) {
ch[p][c] = ++tot;
fa[tot] = p;
}
p = ch[p][c];
}
}
}
std::queue<int> que;
inline void calfail() {
for(int i = 0; i < 26; i++) {
if(ch[0][i]) {
gra[0].push_back(ch[0][i]);
que.push(ch[0][i]);
}
}
while(!que.empty()) {
int u = que.front(); que.pop();
for(int i = 0; i < 26; i++) {
if(ch[u][i]) {
int p = fail[u];
while(p && !ch[p][i]) p = fail[p];
fail[ch[u][i]] = ch[p][i];
gra[ch[p][i]].push_back(ch[u][i]);
que.push(ch[u][i]);
}
}
}
}
int dfn[MAXN], siz[MAXN], clk;
void dfs(int u) {
dfn[u] = ++clk; siz[u] = 1;
for(int i = 0; i < gra[u].size(); i++) {
int v = gra[u][i];
dfs(v);
siz[u] += siz[v];
}
}
int tr[MAXN];
inline int lowbit(int x) {
return x & -x;
}
inline void add(int x, int v) {
for(int i = x; i <= clk; i += lowbit(i)) {
tr[i] += v;
}
}
inline int query(int x) {
int res = 0;
for(int i = x; i; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
inline void solve() {
int p = 0, cnt = 0;
for(int i = 1; i <= n; i++) {
if(op[i] == 'P') {
cnt++;
for(int j = 0; j < vec[cnt].size(); j++) {
int x = vec[cnt][j].first;
ans[vec[cnt][j].second] += query(dfn[end[x]] + siz[end[x]] - 1) - query(dfn[end[x]] - 1);
}
} else if(op[i] == 'B') {
add(dfn[p], -1);
p = fa[p];
} else {
int c = op[i] - 'a';
p = ch[p][c];
add(dfn[p], 1);
}
}
}
int main() {
scanf("%s%d", op + 1, &m); n = strlen(op + 1);
for(int i = 1, x, y; i <= m; i++) {
scanf("%d%d", &x, &y);
vec[y].push_back(PII(x, i));
}
insert();
calfail();
dfs(0);
solve();
for(int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}