題目實際上要求我們求從每個點出發的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 的一種方式。乙個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比...