選取字串 60pts
直接暴力60pts;
這題難點在於讀懂題把。。。
考慮建出 $ KMP $ 樹,然後在其中選出 $ k $ 個數,他們的 $ LCA $ 的深度的平方和就是這個答案,然後簡單統計一下即可;
具體地,把 $ KMP $ 樹建出來,然後求每 $ k $ 個點的 $ LCA $ 的深度的平方和即可,最後乘上方案數(總的減去每棵子樹的);
直接列舉 $ LCA $ 即可;
時間複雜度:$ \Theta(n) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const long long mod = 998244353;
int k;
int n;
char s[1000005];
long long fac[1000005], fav[1000005];
inline long long ksm(long long a, long long b) {
long long ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
inline long long C(long long a, long long b) {
if (a < b) return 0;
if (b < 0) return 0;
return fac[a] * fav[b] % mod * fav[a - b] % mod;
}
long long ans;
int pi[1000005];
struct sss{
int t, ne;
}e[2000005];
int h[2000005], cnt;
inline void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
inline void KMP() {
int j = 0;
add(0, 1);
for (int i = 2; i <= n; i++) {
while(j && s[i] != s[j + 1]) j = pi[j];
if (s[i] == s[j + 1]) j++;
pi[i] = j;
add(j, i);
}
}
int dep[1000005], siz[1000005];
long long sum[1000005];
void dfs(int x, int de) {
dep[x] = de;
siz[x] = 1;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
dfs(u, de + 1);
siz[x] += siz[u];
sum[x] = (sum[x] + C(siz[u], k)) % mod;
}
}
int main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> k;
cin >> (s + 1);
n = strlen(s + 1);
fac[0] = 1;
fav[0] = 1;
for (int i = 1; i <= n + 1; i++) {
fac[i] = fac[i - 1] * i % mod;
fav[i] = ksm(fac[i], mod - 2);
}
KMP();
dfs(0, 1);
for (int i = 0; i <= n; i++) {
if (siz[i] < k) continue;
long long res = (C(siz[i], k) - sum[i] + mod) % mod;
ans = (ans + res * dep[i] % mod * dep[i] % mod) % mod;
}
cout << ans;
return 0;
}
取石子 10pts
考慮如果是有奇數個,那麼直接取 $ 1 $ 個先手必勝;
那麼擴充套件一下,發現我們先求出異或和 $ sum $ ,然後如果 $ lowbit(sum) \leq k $ 那麼我們就能選這一位,然後累加繼續判斷,否則直接 $ break $ 即可;
我們對於每個數都進行一邊這個操作,時間複雜度 $ \Theta(n \log w) $,其中 $ w $ 為值域;
具體看程式碼;
點選檢視程式碼
#include <iostream>
#include <cstdio>
using namespace std;
int n, k;
int a[100005];
int main() {
freopen("nim.in", "r", stdin);
freopen("nim.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum ^= a[i];
}
if (!sum || (sum & (-sum)) > k) {
cout << 0;
return 0;
}
cout << 1 << '\n';
for (int i = 1; i <= n; i++) {
int p = 0;
int now = sum;
for (int j = 0; j <= 30; j++) {
if ((now >> j) & 1) {
now ^= (a[i] - p);
p += (1 << j);
if (p > a[i] || p > k) break;
now ^= (a[i] - p);
cout << i << ' ' << p << '\n';
}
}
}
return 0;
}
均衡區間 0pts
賽時想出正解然後全RE,ACCODERS上還被卡常了。。。
我們先維護出一個數前面和後面第一個小於它的和大於它的,這個可以單調棧維護;
主要講一下這個單調棧,這裡用線段樹會T掉;
我們維護一個單調遞增的棧,然後棧頂就是第一個小於它的,反之同理;
考慮為什麼是對的,因為這樣會將中間的小於它的全丟掉,這些數對後面的沒有貢獻,而且這樣會盡可能的將最值往後移,所以是對的;
考慮後面的操作,我們發現,對於一個數 $ a_i $,設它後面的最大的第一個小於它的和大於它的的最大的位置為 $ R_i $,前面的為 $ L_i $,當它做左端點時,只有它滿足題意的對應的右端點範圍為 $ [R_i + 1, n] $,反之如果它做右端點,只有它滿足題意的左端點的範圍為 $ [1, L_i - 1] $,那麼我們要找兩者都滿足條件的,直接將這些區間排序後上掃描線即可;
掃描線部分具體看程式碼;
時間複雜度:$ \Theta(n \log n) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define FI(n) FastIO::read(n)
#define FO(n) FastIO::write(n)
#define Flush FastIO::Fflush()
namespace FastIO {
const int SIZE = 1 << 16;
char buf[SIZE], obuf[SIZE], str[60];
int bi = SIZE, bn = SIZE, opt;
inline int read(register char *s) {
while(bn) {
for (; bi < bn && buf[bi] <= ' '; bi = -~bi);
if (bi < bn) break;
bn = fread(buf, 1, SIZE, stdin);
bi &= 0;
}
register int sn=0;
while(bn) {
for (; bi < bn && buf[bi] > ' '; bi = -~bi) s[sn++] = buf[bi];
if(bi < bn) break;
bn = fread(buf,1,SIZE,stdin);
bi &= 0;
}
s[sn] &= 0;
return sn;
}
inline bool read(register int &x){
int n = read(str), bf = 0;
if(!n) return 0;
register int i=0;
(str[i] == '-') && (bf = 1, i = -~i);
(str[i] == '+') && (i = -~i);
for (x = 0; i < n; i = -~i) x = (x << 3) + (x << 1) + (str[i] ^ 48);
bf && (x = ~x + 1);
return 1;
}
inline bool read(register long long &x) {
int n = read(str), bf = 1;
if(!n) return 0;
register int i=0;
(str[i] == '-') && (bf = -1,i = -~i);
for (x = 0; i < n; i= -~i) x = (x << 3) + (x << 1) + (str[i] ^ 48);
(bf < 0) && (x = ~x + 1);
return 1;
}
inline void write(register int x) {
if(!x) obuf[opt++] = '0';
else {
(x < 0) && (obuf[opt++] = '-', x = ~x + 1);
register int sn = 0;
while(x) str[sn++] = x % 10 + '0', x /= 10;
for (register int i = sn - 1; i >= 0; i = ~-i) obuf[opt++] = str[i];
}
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void write(register long long x) {
if(!x) obuf[opt++] = '0';
else {
(x < 0) && (obuf[opt++] = '-', x = ~x + 1);
register int sn = 0;
while(x) str[sn++] = x % 10 + '0', x /= 10;
for (register int i = sn - 1; i >= 0; i = ~-i) obuf[opt++] = str[i];
}
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void write(register unsigned long long x){
if(!x) obuf[opt++] = '0';
else {
register int sn=0;
while(x) str[sn++] = x % 10 + '0', x /= 10;
for (register int i = sn - 1 ; i >= 0 ; i = ~-i)obuf[opt++] = str[i];
}
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void write(register char x) {
obuf[opt++] = x;
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void Fflush(){
opt && fwrite(obuf, 1, opt, stdout);
opt &= 0;
}
}
int n, id;
int a[1000005], ans[1000005];
pair<int, int> L[1000005], R[1000005];
struct sss{
int l, r;
}e[1000005];
namespace BIT{
inline int lowbit(int x) {
return x & (-x);
}
int tr[1000005];
inline void add(int pos, int d) {
for (int i = pos; i <= n; i += lowbit(i)) tr[i] += d;
}
inline int ask(int pos) {
int an = 0;
for (int i = pos; i; i -= lowbit(i)) an += tr[i];
return an;
}
}
int st[1000005], s[1000005], cnt, tot;
int main() {
freopen("interval.in", "r", stdin);
freopen("interval.out", "w", stdout);
FI(n); FI(id);
for (int i = 1; i <= n; i++) {
FI(a[i]);
}
for (int i = 1; i <= n; i++) {
while(cnt > 0 && a[i] <= a[st[cnt]]) cnt--;
while(tot > 0 && a[i] >= a[s[tot]]) tot--;
e[i].r = min(st[cnt], s[tot]);
st[++cnt] = i;
s[++tot] = i;
}
cnt = tot = 0;
for (int i = n; i >= 1; i--) {
while(cnt > 0 && a[i] <= a[st[cnt]]) cnt--;
while(tot > 0 && a[i] >= a[s[tot]]) tot--;
e[i].l = max(st[cnt], s[tot]);
if (cnt == 0 || tot == 0) e[i].l = 2e9;
st[++cnt] = i;
s[++tot] = i;
}
for (int i = 1; i <= n; i++) {
L[i] = {e[i].l, i};
R[i] = {e[i].r, i};
}
sort(L + 1, L + 1 + n, greater<pair<int, int> >());
sort(R + 1, R + 1 + n);
for (int i = 1; i <= n; i++) BIT::add(i, 1);
int now = 1;
for (int i = 1; i <= n; i++) {
while(now <= n && R[now].first < i) {
BIT::add(R[now].second, -1);
now++;
}
if (e[i].l > n) FO(0);
else FO(BIT::ask(n) - BIT::ask(e[i].l - 1));
FO(' ');
}
FO('\n');
while(now <= n) BIT::add(R[now].second, -1);
now = 1;
for (int i = 1; i <= n; i++) BIT::add(i, 1);
for (int i = n; i >= 1; i--) {
while(now <= n && L[now].first > i) {
BIT::add(L[now].second, -1);
now++;
}
if (e[i].r < 1) continue;
ans[i] = BIT::ask(e[i].r);
}
for (int i = 1; i <= n; i++) {
FO(ans[i]);
FO(' ');
}
Flush;
return 0;
}