日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

【WC2016】论战捆竹竿

發(fā)布時(shí)間:2025/7/14 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【WC2016】论战捆竹竿 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

已經(jīng)快三周了啊……終于把坑填了……

首先顯然是把除了自身的所有border拿出來(lái),即做 \(\left\{ n - b_1, n - b_2, \dots, n - b_k, n \right\}\) 的完全背包。但是值域很大,所以考慮同余最短路。

首先,由 [國(guó)家集訓(xùn)隊(duì)]墨墨的等式 的經(jīng)驗(yàn),顯然可以 \(O(n^2)\),因?yàn)樽铋L(zhǎng)border可以很小,所以可以卡到滿。

然后就要一個(gè)性質(zhì):一個(gè)串的border可以分成 \(O\left( \log n \right)\) 個(gè)等差數(shù)列。證明咕咕咕。

有了這個(gè),就可以干一些有趣的事:

考慮一個(gè)有趣的暴力,每次更新一個(gè)首項(xiàng)為 \(x\),公差為 \(d\) 的等差數(shù)列時(shí),可以把點(diǎn)按模 \(d\) 剩余系分類,這樣子dp更新的區(qū)間是連續(xù)的。線段樹搞搞,因?yàn)闆]有負(fù)權(quán)所以可以跑dijkstra,然后同樣可以不保存邊。最后再像之前那題的經(jīng)驗(yàn),每個(gè)等差數(shù)列分別更新。這樣復(fù)雜度兩個(gè) \(\log\),空間 \(O\left(n\right)\)。(來(lái)自官方題解)

然而這樣做復(fù)雜度還是太大了。能不能不用線段樹啊?

我們要將一個(gè)等差數(shù)列有效邊壓到 \(O(n)\),那么也就剩余系有希望了。

觀察到一個(gè)等差數(shù)列是 \(\left[x, x + d, x + 2d, \dots\right]\),可以考慮模 \(x\),然后在模 \(x\) 的剩余系下轉(zhuǎn)移,顯然還是構(gòu)成了一堆環(huán)。

先考慮如果是在模 \(x\) 意義下的情況的DP轉(zhuǎn)移,注意到等差數(shù)列長(zhǎng)度的限制,轉(zhuǎn)移點(diǎn)不能太遠(yuǎn),因此考慮單調(diào)隊(duì)列維護(hù)(也就是滑動(dòng)窗口類似的)。

然后考慮如何在模 \(P\) 意義下轉(zhuǎn)換為模 \(Q\)

如果直接將所有能搞出的方案放進(jìn)去,發(fā)現(xiàn)能表示出來(lái)的都是 \(dis_i + k \cdot P\) 類型的,這啟發(fā)我們先把所有 \(dis_i\) 扔到模 \(Q\) 意義下序列上,然后用 \(P\) 更新同余最短路,這樣同樣能表示出 \(dis_i + k \cdot P\) 了。

所以找出所有等差數(shù)列后,對(duì)于每個(gè)數(shù)列分別先轉(zhuǎn)換模數(shù),再做dp轉(zhuǎn)移。注意這道題卡常數(shù)(該優(yōu)化的都優(yōu)化上去就好了,比如下標(biāo)加的時(shí)候用取模優(yōu)化)。

我寫掛一堆地方:直接拿border長(zhǎng)度上去DP,單調(diào)隊(duì)列少個(gè)等號(hào),轉(zhuǎn)換模數(shù)時(shí)用了 \(i\) 而不是 \(dis_i\) 初始化……

#include <bits/stdc++.h> struct data {int x, d, c;data() { x = d = c = 0; }bool ins(int v) {if (!c) return x = v, d = 0, c = 1, true;if (!d) return d = v - x, c = 2, true;if (x + c * d == v) return ++c, true;return false;} } ; const int MAXN = 500010; typedef long long LL; const LL INF = 0x3f3f3f3f3f3f3f3fLL; typedef std::vector<data> V; struct KMP {int nxt[MAXN];V build(char * buf, int len) {for (int i = 1; i <= len; ++i) {int now = nxt[i - 1];while (now && buf[now + 1] != buf[i]) now = nxt[now];nxt[i] = buf[now + 1] == buf[i] && now + 1 != i ? now + 1 : 0;}V res; data rt; int now = nxt[len];while (now) {if (!rt.ins(now))res.push_back(rt), (rt = data()).ins(now);now = nxt[now];}if (rt.x) res.push_back(rt);for (auto & t : res)t.x = len - t.x, t.d = -t.d;std::reverse(res.begin(), res.end());return res;} } kmp;int n; LL W; LL dis[MAXN]; int mod; void getmax(LL & x, LL y) { x < y ? x = y : 0; } void getmin(LL & x, LL y) { x > y ? x = y : 0; } int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } void trans(int v) {static LL d2[MAXN];memset(d2, 0x3f, v << 3);for (int i = 0; i < mod; ++i) getmin(d2[dis[i] % v], dis[i]);const int G = gcd(v, mod);for (int i = 0; i < G; ++i) {int at = i;int tm = mod % v;for (int j = (i + mod) % v; j != i; j += tm - v, j += j >> 31 & v)if (d2[j] < d2[at]) at = j;LL now = d2[at] + mod;for (int j = (at + mod) % v; j != at; j += tm - v, j += j >> 31 & v)getmin(d2[j], now), now = std::min(now, d2[j]) + mod;}mod = v;memcpy(dis, d2, mod << 3); } struct qnode {LL v; int at;qnode() { v = at = 0; }qnode(LL x, int y) { v = x, at = y; } } Q[MAXN]; void dp(int v, int D, int C) {trans(v);const int G = gcd(v, D);for (int i = 0; i < G; ++i) {int at = i;int tm = D % v;for (int j = (i + D) % v; j != i; j += tm - v, j += j >> 31 & v)if (dis[j] < dis[at]) at = j;int qb = 1, qe = 0;for (int j = at, k = 0; k * G != v; j += tm - v, j += j >> 31 & v, ++k) {while (qb <= qe && Q[qb].at + C <= k) ++qb;if (qb <= qe) {auto x = Q[qb];getmin(dis[j], v + x.v + (k - x.at) * D);}while (qb <= qe && Q[qe].v + (k - Q[qe].at) * D >= dis[j]) --qe;Q[++qe] = (qnode) {dis[j], k};}} }int main() {std::ios_base::sync_with_stdio(false), std::cin.tie(0);int T; std::cin >> T;while (T --> 0) {static char buf[MAXN];std::cin >> n >> W >> buf + 1; W -= n;mod = n; memset(dis, 0x3f, n << 3); *dis = 0;for (auto t : kmp.build(buf, n)) dp(t.x, t.d ? t.d : 1, t.c);LL ans = 0;for (int i = 0; i != mod; ++i)dis[i] <= W ? ans += (W - dis[i]) / mod + 1 : 0;std::cout << ans << std::endl;}return 0; }

轉(zhuǎn)載于:https://www.cnblogs.com/daklqw/p/11606992.html

總結(jié)

以上是生活随笔為你收集整理的【WC2016】论战捆竹竿的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。