The 16th Heilongjiang Provincial Collegiate Programming Contest
2021 黑龙江省赛
A: And RMQ
线段树区间与 ->1、维护区间或,复杂度可大可小
2、吉司机线段树
https://codeforces.com/gym/103107/problem/A
const int maxn = 4e5 + 10;
int a[maxn];
int n, m;
struct seg{
int t[maxn << 2], orr[maxn << 2];
#define root 1, 1, n
#define lr rt << 1
#define rr rt << 1 | 1
#define mid (l + r >> 1)
#define lson lr, l, mid
#define rson rr, mid + 1, r
inline void pushup(int rt) {
t[rt] = max(t[lr], t[rr]);
orr[rt] = orr[lr] | orr[rr];
}
void build(int rt, int l, int r) {
if(l == r) {
t[rt] = orr[rt] = a[l];
return ;
}
build(lson); build(rson);
pushup(rt);
}
//维护区间或,显然这个值或区间所有数都是区间的数,那么如果v&区间或不变,代表v比区间或更大,不需要更新
void ord(int rt, int l, int r, int L, int R, int v) {
if(l == r) {
t[rt] = orr[rt] &= v;
return ;
}
if(L <= mid && (orr[lr] & v) < orr[lr]) ord(lson, L, R, v);
if(R > mid && (orr[rr] & v) < orr[rr]) ord(rson, L, R, v);
pushup(rt);
}
void update(int rt, int l, int r, int pos, int v) {
if(l == r) {
t[rt] = orr[rt] = v;
return ;
}
if(pos <= mid) update(lson, pos, v);
else update(rson, pos, v);
pushup(rt);
}
int query(int rt, int l, int r, int L, int R) {
if(l >= L && r <= R) return t[rt];
int res = 0;
if(L <= mid) res = query(lson, L, R);
if(R > mid) res = max(res, query(rson, L, R));
return res;
}
}T;
signed main() {
n = rd(), m = rd();
for(int i = 1; i <= n; ++ i) a[i] = rd();
T.build(root);
for(int i = 1; i <= m; ++ i) {
char op[4]; scanf("%s", op);
int l = rd(), r = rd(), v;
if(op[0] == 'A') {
v = rd(); T.ord(root, l, r, v);
}
else if(op[0] == 'U') T.update(root, l, r);
else printf("%d\n", T.query(root, l, r));
}
return 0;
}
D:Doin' Time
最简单区间dp裸题
E: Elastic Search
AC自动机裸题
将所有串插入trie树并建立AC自动机,在建立trie树时使得fa[u]为u的父亲节点。
由fail数组与trie的性质可知,fa[u]与fail[u]皆为结点u对应字符串的一部分。
可以用dp[u]表示以结点u对应的字符串为终点的最长嵌套的长度,
则当前结点u的最长嵌套长度为其对应的父亲结点与对应fail数组取大值、再加上该节点所对应的模式串数量,
即\(dp[u]=max(dp[fail[u]],dp[fa[u]])+cntword[u]\)
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int trie[maxn][26];
int cntword[maxn];
int fail[maxn];
int fa[maxn];
int cnt=0;
int dp[maxn];
int maxx;
void insertwords(string s) {
int root=0;
for(int i=0; i<s.size(); i++) {
int next=s[i]-'a';
if(!trie[root][next]) {
trie[root][next]=++cnt;
}
fa[trie[root][next]]=root;
root=trie[root][next];
}
cntword[root]++;
}
void getfail() {
queue<int>q;
fail[0]=0;
for(int i=0; i<26; i++) {
if(trie[0][i]) {
fail[trie[0][i]]=0;
q.push(trie[0][i]);
}
}
while(!q.empty()) {
int now=q.front();
q.pop();
for(int i=0; i<26; i++) {
if(trie[now][i]) {
fail[trie[now][i]]=trie[fail[now]][i];
q.push(trie[now][i]);
} else {
trie[now][i]=trie[fail[now]][i];
}
}
}
}
int query(string s) {
int now = 0, res = 0, len = s.size();
for (int i = 0; i < len; i++) {
now = trie[now][s[i] - 'a'];
int temp = now;
while (temp != 0&&cntword[temp]!=-1) {
res += cntword[temp];
cntword[temp] = -1;
temp = fail[temp];
}
}
return res;
}
int vis[maxn];
void bfs() {
int root=0;
queue<int>q;
q.push(root);
while(!q.empty()){
root=q.front();
q.pop();
for(int i=0;i<26;i++){
if(trie[root][i]&&!vis[trie[root][i]]){
dp[trie[root][i]]=max(dp[fa[trie[root][i]]],dp[fail[trie[root][i]]])+cntword[trie[root][i]];
q.push(trie[root][i]);
vis[trie[root][i]]=1;
maxx=max(maxx,dp[trie[root][i]]);
}
}
}
}
int main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
cnt=0;
int n;
string s;
cin >> n;
for(int i=0; i<n; i++) {
cin >> s ;
insertwords(s);
}
getfail();
bfs();
cout<<maxx<<endl;
}
F:Function
欧拉筛的运用
G:Go? No
tarjan求割点连的最多桥数
H:Hack DSU!
简单思维
L:Labi-Ribi
典中典贪心,生命值还能是负数,真是典中典
就是打怪兽模型,怪兽生命值\(h_i\),打一只怪攻击力增加\(c_i\),大于等于\(h_i\)才能攻击,问一开始最少攻击力
显然对于\(c_i >= 0\),直接按h小的贪,对于\(c_i < 0\),按\(h_i+c_i\)大的贪即可,最后排序后就是求\(max_{i = 1}^n(h - pre_{~i - 1})\)
q = 1000,可以nq做法,q = 1e5,要离线线段树求区间最大值
struct node{
int h, c;
bool operator < (const node &x) const {
if(c >= 0) return h < x.h;
else return h + c > x.h + x.c;
}
}a[maxn], b[maxn];
int h[maxn];
void run() {
int n = rd(), acnt = 0, bcnt = 0;
for(int i = 1; i <= n; ++ i) h[i] = rd();
for(int i = 1; i <= n; ++ i) {
int ta = rd(), tb = rd(), tc = tb - ta;
if(tc >= 0) b[bcnt ++] = {h[i], tc};
else a[acnt ++] = {h[i], tc};
}
sort(a, a + acnt); sort(b, b + bcnt);
int ans = -0x3f3f3f3f3f3f3f3f, now = 0;
for(int i = 0; i < bcnt; ++ i) {
ans = max(ans, b[i].h - now);
now += b[i].c;
}
for(int i = 0; i < acnt; ++ i) {
ans = max(ans, a[i].h - now);
now += a[i].c;
}
printf("%lld\n", ans);
int q = rd();
for(int i = 1; i <= q; ++ i) {
int th = rd(), ta = rd(), tb = rd();
if(tb - ta >= 0) {
node tmp = {th, tb - ta};
int pos = lower_bound(b, b + bcnt, tmp) - b;
for(int j = bcnt; j > pos; -- j) b[j] = b[j - 1];
b[pos] = tmp; bcnt ++;
}
else {
node tmp = {th, tb - ta};
int pos = lower_bound(a, a + acnt, tmp) - a;
for(int j = acnt; j > pos; -- j) a[j] = a[j - 1];
a[pos] = tmp; acnt ++;
}
ans = -0x3f3f3f3f3f3f3f3f, now = 0;
for(int j = 0; j < bcnt; ++ j) {
ans = max(ans, b[j].h - now);
now += b[j].c;
}
for(int j = 0; j < acnt; ++ j) {
ans = max(ans, a[j].h - now);
now += a[j].c;
}
printf("%lld\n", ans);
}
return ;
}
更新线段树的写法
struct node{
int h, c, id;
bool operator < (const node &a) const {
if(c >= 0 && a.c >= 0) return h < a.h;
else if(c >= 0 || a.c >= 0) return c > a.c;
else return h + c > a.h + a.c;
}
}a[maxn];
int id[maxn], pos[maxn];
//维护max(h[i] - sufsum[i - 1])
//每个节点有sum[i]是子区间权值v的和,mx[i]是子区间最大值
struct seg{
int t[maxn << 2], sum[maxn << 2], lz[maxn << 2];
#define root 1, 1, n + q
#define lr rt << 1
#define rr rt << 1 | 1
#define mid (l + r >> 1)
#define lson lr, l, mid
#define rson rr, mid + 1, r
inline void push(int rt) {
t[rt] = max(t[lr], t[rr]);
sum[rt] = sum[lr] + sum[rr];
}
inline void upd(int rt, int v) {
lz[rt] += v;
t[rt] += v;
}
inline void down(int rt) {
if(!lz[rt]) return ;
upd(lr, lz[rt]); upd(rr, lz[rt]);
lz[rt] = 0;
}
void build(int rt, int l, int r) {
if(l == r) {
t[rt] = -0x3f3f3f3f3f3f3f3f;
sum[rt] = 0; return ;
}
build(lson); build(rson);
push(rt);
}
void update(int rt, int l, int r, int pos, int v) {
if(l == r) {
t[rt] = v;
sum[rt] = a[id[l]].c;
return ;
}
down(rt);
if(pos <= mid) update(lson, pos, v);
else update(rson, pos, v);
push(rt);
}
void update(int rt, int l, int r, int L, int R, int v) {
if(L > R) return ;
if(l >= L && r <= R) return upd(rt, v), void();
down(rt);
if(L <= mid) update(lson, L, R, v);
if(R > mid) update(rson, L, R, v);
push(rt);
}
int query(int rt, int l, int r, int L, int R) {
if(L > R) return 0;
if(l >= L && r <= R) return sum[rt];
down(rt);
int res = 0;
if(L <= mid) res += query(lson, L, R);
if(R > mid) res += query(rson, L, R);
return res;
}
}T;
void run() {
int n = rd(), t1, t2;
for(int i = 1; i <= n; ++ i) a[i].h = rd();
for(int i = 1; i <= n; ++ i) t1 = rd(), t2 = rd(), a[i].c = t2 - t1, id[i] = i;
int q = rd();
for(int i = 1; i <= q; ++ i)
a[n + i].h = rd(), t1 = rd(), t2 = rd(), a[n + i].c = t2 - t1, id[n + i] = n + i;
for(int i = 1; i <= n + q; ++ i) a[i].id = i;
sort(id + 1, id + 1 + n + q, [](int x, int y) {return a[x] < a[y];});
for(int i = 1; i <= n + q; ++ i) pos[id[i]] = i;
T.build(root);
for(int i = 1; i <= n; ++ i) {
int npos = pos[i];
T.update(root, npos, a[i].h - T.query(root, 1, npos - 1));
T.update(root, npos + 1, n + q, -a[i].c);
} printf("%lld\n", T.t[1]);
for(int i = 1; i <= q; ++ i) {
int npos = pos[i + n];
T.update(root, npos, a[i + n].h - T.query(root, 1, npos - 1));
T.update(root, npos + 1, n + q, -a[i + n].c);
printf("%lld\n", T.t[1]);
}
return ;
}

浙公网安备 33010602011771号