dp 出所有相邻两个位置的 lcp,然后即可优化 cmp。 Θ ( n log n ) \Theta(n\log n) Θ(nlogn)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = 1000010;
int n;
int lcp[N], v[N], rk[N];
char s[N];
bool cmp(int x, int y) {
if (x < y) {
int l = lcp[x];
if (l >= y - x)
return false;
return s[x + l + 1] < s[x + l];
}
int l = lcp[y];
if (l >= x - y)
return false;
return s[y + l] < s[y + l + 1];
}
int main() {
#ifdef LBT
freopen("test.in", "r", stdin);
int nol_cl = clock();
#endif
scanf("%d%s", &n, s + 1);
for (int i = n - 1; i; --i)
lcp[i] = (s[i] != s[i + 1]) ? 0 : (1 + lcp[i + 1]);
for (int i = 1; i <= n; ++i)
v[i] = i;
stable_sort(v + 1, v + n + 1, cmp);
for (int i = 1; i <= n; ++i)
rk[v[i]] = i;
for (int i = 1; i <= n; ++i)
printf("%d ", v[i]);
#ifdef LBT
LOG("Time: %dms\n", int ((clock()
-nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
return 0;
}
先把所有不同的 gcd ( P , Q ) \gcd(P,Q) gcd(P,Q) 剩余类分开处理,根据 CRT 可以发现将每个数可以乘以那个转换到   m o d   P ′ Q ′ \bmod P'Q' modP′Q′ 的定值,sort 后扫一下即可。 Θ ( n log n ) \Theta(n\log n) Θ(nlogn)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
const int N = 1000010;
int p, q, n, m, rp, rq, g;
bool a[N], b[N];
ll ca[N];
ll t;
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
void exGcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1;
y = 0;
return;
}
exGcd(b, a % b, y, x);
y -= a / b * x;
}
int inv(int a, int p) {
int x, y;
exGcd(a, p, x, y);
return x < 0 ? x + p : x;
}
int main() {
#ifdef LBT
freopen("test.in", "r", stdin);
int nol_cl = clock();
#endif
scanf("%d%d%d%d%lld", &p, &q, &n, &m, &t);
while (n--) {
int x;
scanf("%d", &x);
a[x] = true;
}
while (m--) {
int x;
scanf("%d", &x);
b[x] = true;
}
g = gcd(p, q);
rp = p / g;
rq = q / g;
ll ans = 0;
ll m = rp * (ll)rq;
ll ip = inv(rq, rp) * (ll)rq, iq = inv(rp, rq) * (ll)rp;
for (int r = 0; r < g && r < t; ++r) {
int cnta = 0, cntb = 0;
for (int i = 0; i < rp; ++i)
if (a[i * g + r])
ca[++cnta] = i * (ll)ip % m;
for (int i = 0; i < rq; ++i)
cntb += b[i * g + r];
ll tt = (t - r + g - 1) / g;
ans += tt / m * cnta * cntb;
tt %= m;
sort(ca + 1, ca + cnta + 1);
for (int i = 0; i < rq; ++i)
if (b[i * g + r]) {
ll cur = i * (ll)iq % m;
ans += lower_bound(ca + 1, ca + cnta + 1, tt - cur) - ca - 1;
ans += lower_bound(ca + 1, ca + cnta + 1, m + tt - cur) - lower_bound(ca + 1, ca + cnta + 1, m - cur);
}
}
printf("%lld\n", ans);
#ifdef LBT
LOG("Time: %dms\n", int ((clock()
-nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
return 0;
}
主席树优化建图费用流。复杂度为什么对啊?好像是 Θ ( n 3 log 2 n ) \Theta(n^3\log^2 n) Θ(n3log2n) 吧?
#include
#include
#include
#include
#include
#define LOG(FMT...) // fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
const int N = 1010, M = 50010;
int n, vc, S, T, w;
int a[N], ver[N], vero[N];
bool inq[M], vis[M], good[M];
int que[M];
ll dis[M];
pair<int, int> p[N];
struct E {
int v, w, c;
E *next, *rev;
};
E* pth[M];
E* g[M];
E* adde(int u, int v, int w, int c) {
static E pool[M * 2], *ptop = pool;
E* p = ptop++;
p->v = v;
p->w = w;
p->c = c;
p->next = g[u];
g[u] = p;
return p;
}
void link(int u, int v, int w, int c) {
if (u == 0 || v == 0) return;
E *p = adde(u, v, w, c), *q = adde(v, u, 0, -c);
p->rev = q;
q->rev = p;
}
struct Node {
int l, r, id;
Node *ls, *rs;
void lk(int u, int k, int w) {
if (k == r) {
link(id, u, 1, w);
return;
}
if (k <= ls->r)
ls->lk(u, k, w);
else {
link(ls->id, u, M, w);
rs->lk(u, k, w);
}
}
};
Node* newNode() {
static Node pool[M], *top = pool;
return top++;
}
Node* build(int l, int r) {
Node* p = newNode();
p->l = l;
p->r = r;
if (l == r)
return p;
int mid = (l + r) >> 1;
p->ls = build(l, mid);
p->rs = build(mid + 1, r);
return p;
}
int newVertex() {
return ++vc;
}
void ins(Node* o, int k, int sgn) {
if (o->l == o->r) {
o->id = newVertex();
link(vero[k], o->id, 1, a[k] * sgn - w);
return;
}
Node* p = (k <= o->ls->r) ? o->ls : o->rs;
ins(p, k, sgn);
o->id = newVertex();
link(o->ls->id, o->id, o->ls->r - o->ls->l + 1, 0);
link(o->rs->id, o->id, o->rs->r - o->rs->l + 1, 0);
}
ll augment() {
LOG("AUGMENT %d\n", vc);
int ql = 0, qr = 0;
que[qr++] = T;
memset(good, 0, sizeof(good));
good[T] = true;
while (ql != qr) {
int u = que[ql++];
for (E* p = g[u]; p; p = p->next)
if (p->rev->w && !good[p->v]) {
good[p->v] = true;
que[qr++] = p->v;
}
}
ql = 0;
qr = 0;
memset(vis, 0, sizeof(vis));
que[qr++] = S;
inq[S] = true;
dis[S] = 0;
vis[S] = true;
pth[S] = NULL;
while (ql != qr) {
int u = que[ql++];
if (ql == M) ql = 0;
inq[u] = false;
for (E* p = g[u]; p; p = p->next)
if (p->w && good[p->v] && (!vis[p->v] || (dis[p->v] > dis[u] + p->c))) {
vis[p->v] = true;
dis[p->v] = dis[u] + p->c;
pth[p->v] = p->rev;
if (!inq[p->v]) {
que[qr++] = p->v;
if (qr == M) qr = 0;
inq[p->v] = true;
}
}
}
if (!vis[T]) {
return 1;
}
ll ret = dis[T];
E* p = pth[T];
while (p) {
++p->w;
--p->rev->w;
p = pth[p->v];
}
return ret;
}
int main() {
scanf("%d%d", &n, &w);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i)
p[i] = make_pair(a[i], i);
sort(p + 1, p + n + 1);
S = newVertex();
T = newVertex();
ll ans = n * (ll)w;
for (int i = 1; i <= n; ++i) {
ver[i] = newVertex();
vero[i] = newVertex();
link(S, ver[i], 1, 0);
link(ver[i], vero[i], 1, 0);
link(vero[i], T, 1, 0);
}
Node *le = build(1, n), *ge = build(1, n);
for (int i = 1; i <= n; ++i) {
int j = p[i].second;
le->lk(ver[j], j, a[j]);
ins(le, j, -1);
}
for (int i = n; i; --i) {
int j = p[i].second;
ge->lk(ver[j], j, -a[j]);
ins(ge, j, 1);
}
ll cost;
while ((cost = augment()) < 0)
ans += cost;
printf("%lld\n", ans);
return 0;
}
某种意义上,显然应当先竖着拿。于是可以记状态 s = ( x , y ) s=(x,y) s=(x,y) 表示当前以及下一个这个位置至少支付几个牌用于偿还前面的横牌。矩阵乘法优化转移。 Θ ( X s 6 log n ) \Theta(X s^6\log n) Θ(Xs6logn),其中 s = 3 s=3 s=3。
#include
#include
using namespace std;
typedef long long ll;
const int N = 1010, L = 60, P = 998244353, S = 15;
int st[L][S][S], tran[N][S][S];
int dp[S];
ll k[N];
int a[N];
ll n;
int c, m;
void trans(int (*mat)[S]) {
static int tmp[S];
memset(tmp, 0, sizeof(tmp));
for (int i = 0; i < S; ++i)
for (int j = 0; j < S; ++j)
tmp[j] = (tmp[j] + dp[i] * (ll)mat[i][j]) % P;
memcpy(dp, tmp, sizeof(dp));
}
int fdiv(int a, int b) {
if (a < 0)
return (a - b + 1) / b;
return a / b;
}
int main() {
scanf("%lld%d%d", &n, &c, &m);
for (int i = 1; i <= m; ++i)
scanf("%lld%d", &k[i], &a[i]);
k[m + 1] = n + 1;
for (int i = 0; i <= c; ++i) {
for (int j = 0; j <= 4; ++j)
for (int k = 0; k <= 2; ++k) {
for (int ad = 0; ad <= 2; ++ad) {
if (ad + j > c) break;
if (ad + j > i)
tran[i][j * 3 + k][(k + ad) * 3 + ad] = ((c - ad - j) / 3 + 1) % P;
else {
int r = (ad + j) % 3;
tran[i][j * 3 + k][(k + ad) * 3 + ad] = ((c - r) / 3 - fdiv(i - r - 1, 3)) % P;
}
}
}
}
memcpy(st[0], tran[0], sizeof(st[0]));
for (int i = 1; i < L; ++i) {
for (int j = 0; j < S; ++j)
for (int k = 0; k < S; ++k)
for (int l = 0; l < S; ++l)
st[i][j][k] = (st[i][j][k] + st[i - 1][j][l] * (ll)st[i - 1][l][k]) % P;
}
dp[0] = 1;
for (int i = 1; i <= m; ++i) {
for (int b = 0; b < L; ++b)
if ((k[i] - k[i - 1] - 1) >> b & 1)
trans(st[b]);
trans(tran[a[i]]);
}
for (int b = 0; b < L; ++b)
if ((n - k[m]) >> b & 1)
trans(st[b]);
printf("%d\n", dp[0]);
return 0;
}
考虑把两个图叠到一起看,就是要消去若干个圈。构造欧拉回路遍历整个棋盘即可。 Θ ( n 2 ) \Theta(n^2) Θ(n2)
#include
#include
#include
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
const int N = 2010;
int n, m, cnt;
char s[N][N], t[N][N];
const char* v = "LURD";
int dx[] = {0, -1, 0, 1}, dy[] = {-1, 0, 1, 0};
int mp[256];
bool vis[N][N];
int vis2[N * N];
char ans[N * N * 2];
int gt[N * N], rvs[N * N];
char w[N * N];
void adde(int u, int v, char ww) {
gt[u] = v;
w[u] = ww;
rvs[v] = u;
}
int gid(int x, int y) { return (x - 1) * m + y; }
void dfs(int u) {
vis2[u] = 1;
if (gt[u] && vis2[gt[u]] != -1) {
if (!vis2[gt[u]])
dfs(gt[u]);
ans[++cnt] = w[u];
}
int vx = (u - 1) / m + 1, vy = (u - 1) % m + 1;
for (int dir = 0; dir < 4; ++dir) {
int x = vx + dx[dir], y = vy + dy[dir];
if (x < 1 || x > n || y < 1 || y > m)
continue;
if (s[x][y] == 'o')
continue;
int dd = mp[s[x][y]];
int gx = x + dx[dd], gy = y + dy[dd];
if (vis2[gid(gx, gy)])
continue;
int cyc = rvs[gid(gx, gy)];
if (cyc == 0) {
ans[++cnt] = v[dd ^ 2];
}
dfs(gid(gx, gy));
ans[++cnt] = v[dir];
}
vis2[u] = -1;
}
int main() {
mp['u'] = 1;
mp['n'] = 3;
mp['<'] = 2;
mp['>'] = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%s", s[i] + 1);
for (int i = 1; i <= n; ++i)
scanf("%s", t[i] + 1);
int px, py;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (s[i][j] == 'o') {
px = i;
py = j;
}
vis[px][py] = true;
int x = px, y = py;
while (t[x][y] != 'o') {
int dir = mp[t[x][y]];
int gx = x + dx[dir], gy = y + dy[dir];
int dd = mp[s[gx][gy]];
adde(gid(x, y), gid(gx + dx[dd], gy + dy[dd]), v[dir]);
x = gx + dx[dd];
y = gy + dy[dd];
vis[x][y] = true;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
if (vis[i][j])
continue;
if (s[i][j] == t[i][j])
continue;
if ((i ^ j) & 1)
continue;
x = i;
y = j;
while (!vis[x][y]) {
vis[x][y] = true;
int dir = mp[t[x][y]];
int gx = x + dx[dir], gy = y + dy[dir];
int dd = mp[s[gx][gy]];
adde(gid(x, y), gid(gx + dx[dd], gy + dy[dd]), v[dir]);
x = gx + dx[dd];
y = gy + dy[dd];
}
}
dfs(gid(px, py));
reverse(ans + 1, ans + cnt + 1);
puts(ans + 1);
return 0;
}
不可做啊?