NOI2016 優秀的拆分

2021-10-03 04:18:34 字數 2680 閱讀 1145

題目實際上要求我們求從每個點出發的aa串的數量

考慮點i的答案,發現如果字首i與字首j(j<=i)的最長公共字尾》=i-j,那麼i點出發向前就存在乙個長度為i-j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件

考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的最長公共字尾為兩個字首點在parent樹上的lca的len長度。考慮在lca處統計貢獻。

於是每個點都建立一顆動態線段樹,要統計子樹之間相互的貢獻,按照啟發式合併合併線段樹,即繼承重兒子的動態線段樹,暴力將輕兒子的線段樹上的節點合併到重兒子,統計貢獻,具體會用到區間加(lazy_tag)和區間查詢,時間複雜度o(tnlog^2)

#include #include #include #include #include #include #include #define mm(a, b) memset(a, b, sizeof(a))

#define ll long long

using namespace std;

const int maxn = 100010;

int len_s = 0;

char s[maxn], rev[maxn];

int ans[maxn], id[maxn * 4], root[maxn * 4], ansl[maxn], ansr[maxn];

int size[maxn * 26], lc[maxn * 26], rc[maxn * 26], tag[maxn * 26];

int vec_tot, sam_tot, seg_tot, head, last_prefix;

vectortree[maxn * 4], tree_suf[maxn];

struct node dot[maxn * 2];

void init()

void pushdown(int h, int l, int r)

if (lc[h])

tag[lc[h]] += tag[h];

if (rc[h])

tag[rc[h]] += tag[h];

tag[h] = 0;

}void insert(int &h, int l, int r, int loc)

int mid = l + r >> 1;

if (loc <= mid)

insert(lc[h], l, mid, loc);

else

insert(rc[h], mid + 1, r, loc);

size[h] = size[lc[h]] + size[rc[h]];

}int query_size(int h, int l, int r, int s, int e)

void query_sum(int h, int l, int r, int loc)

void add(int h, int l, int r, int s, int e)

int mid = l + r >> 1;

if (e <= mid)

add(lc[h], l, mid, s, e);

else if (s > mid)

add(rc[h], mid + 1, r, s, e);

else

}void create_sam(int c)

last_prefix = cur;

if (!tmp)

dot[cur].fa = head;

else if (dot[tmp].len + 1 == dot[to].len)

dot[cur].fa = to;

else

}}void add_edge()

}void calc_tree(int h)

}for (int i = 0; i < (int)tree[h].size(); ++i)

if ((tmp = min(node + dot[h].len, len_s)) >= node + 1)

}for (int j = 0; j < (int)tree_suf[id[to]].size(); ++j)

}//¿¼âç²»öüëùöâ, »¹óð½úµã±¾éíîªç°×ºµäçé¿ö

for (int i = 0; i < (int)tree_suf[id[h]].size(); ++i)

if ((tmp = min(node + dot[h].len, len_s)) >= node + 1)

insert(root[maxid], 1, len_s, node);

tree_suf[id[maxid]].push_back(node);

}root[h] = root[maxid];

id[h] = id[maxid];

}void arrange()

add_edge();

calc_tree(head);

for (int i = 0; i < (int)tree_suf[id[head]].size(); ++i)

}void clear()

}vec_tot = sam_tot = seg_tot = 0;

}void file()

int main()

printf("%lld\n", sum);

}return 0;

}

NOI2016 優秀的拆分

看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...

NOI2016 優秀的拆分

點此看題 首先轉化問題,我們可以求出a i b i a i b i a i b i 即以i ii結束 開始的aaaa aa串的數量,這樣答案就可以表示為 a i b i 1 sum a i times b i 1 a i b i 1 求這兩個陣列,可以隔距離len lenle n設定乙個點,這樣乙個...

NOI2016 優秀的拆分

如果乙個字串可以被拆分為 aabb 的形式,其中 a 和 b 是任意非空字串,則我們稱該字串的這種拆分是優秀的。例如,對於字串 aabaabaa,如果令 a mathrm b mathrm 我們就找到了這個字串拆分成 aabb 的一種方式。乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比...