solved 3 (261/679)
卡了题意,很难受
B Ch’s gift (LCA 或 树链剖分 + 线段树)
思路:只要把主席树节点统计个数的意义改为累计所管辖区间的和就行了。剩下的部分就是裸的树上主席树了。
#include<bits/stdc++.h> using namespace std; #define MAXN 100010 #define LOGN 20 #define MAXQ 100010 #define LL long long typedef struct { int to, next; } Edge; LL v[MAXN], dv[MAXN * 3], n, q, dvcnt; LL par[MAXN][LOGN], depth[MAXN]; Edge edges[MAXN * 2]; int head[MAXN], ecnt; void init() { memset(head, -1, sizeof(head)); ecnt = 0; dvcnt = 0; } void add(int from, int to) { edges[ecnt].to = to; edges[ecnt].next = head[from]; head[from] = ecnt++; } void dfs4lca(int root, int f) { par[root][0] = f, depth[root] = depth[f] + 1; for(int i = head[root]; ~i; i = edges[i].next) { int to = edges[i].to; if(to != f)dfs4lca(to, root); } } void prepare4lca() { dfs4lca(1, 0); for(int d = 0; d + 1 < LOGN; ++d) { for(int i = 1; i <= n; ++i) { if(par[i][d] == 0)par[i][d + 1] = 0; else par[i][d + 1] = par[par[i][d]][d]; } } } int lca(int a, int b) { if(depth[a] < depth[b])swap(a, b); for(int i = LOGN - 1; i >= 0; i--) { int dif = depth[a] - depth[b]; if((dif >> i) & 1)a = par[a][i]; } if(a == b)return a; for(int i = LOGN - 1; i >= 0; --i) { if(par[a][i] != par[b][i]) { a = par[a][i], b = par[b][i]; } } return par[a][0]; } typedef struct { struct Node { int lch, rch; LL val; } nodes[MAXN * 20]; int root[MAXN], ncnt; void init() { memset(nodes, 0, sizeof(nodes)); memset(root, 0, sizeof(root)); ncnt = 0; } private: void update0(int& croot, int proot, LL val, int idx, int l, int r) { if(croot == 0) { croot = ++ncnt; nodes[croot].val = nodes[proot].val + val; } if(l == r)return; int mid = (l + r) >> 1; if(idx <= mid) { nodes[croot].rch = nodes[proot].rch; update0(nodes[croot].lch, nodes[proot].lch, val, idx, l, mid); } else { nodes[croot].lch = nodes[proot].lch; update0(nodes[croot].rch, nodes[proot].rch, val, idx, mid + 1, r); } } public: void insert(int x, LL val, int idx) { update0(root[x], root[par[x][0]], val, idx, 1, dvcnt); } private: LL query0(int qlrt, int qrrt, int da, int db, int l, int r) { if(l >= da && r <= db)return nodes[qrrt].val - nodes[qlrt].val; int mid = (l + r) >> 1; LL res = 0; if(da <= mid)res += query0(nodes[qlrt].lch, nodes[qrrt].lch, da, db, l, mid); if(db > mid)res += query0(nodes[qlrt].rch, nodes[qrrt].rch, da, db, mid + 1, r); return res; } public: LL query(int ql, int qr, int da, int db, bool left_close) { // printf("%d %d ", ql, par[ql][0]); if(left_close)return query0(root[par[ql][0]], root[qr], da, db, 1, dvcnt); else return query0(root[ql], root[qr], da, db, 1, dvcnt); } } PerSegTree; struct qqq { int ql, qr, a, b; } qs[MAXQ]; #define indexOf(x) (lower_bound(dv + 1, dv + dvcnt + 1, x) - dv) PerSegTree pst; void buildPST(int root, int f) { pst.insert(root, v[root], indexOf(v[root])); for(int i = head[root]; ~i; i = edges[i].next) { int to = edges[i].to; if(to != f)buildPST(to, root); } } template<class T> inline void read(T& x) { bool s = 0; char t; while((t = getchar()) != '-' && (t < '0' || t > '9')); if(t == '-')s = 1, t = getchar(); x = t - '0'; while((t = getchar()) >= '0' && t <= '9')x = x * 10 + (t ^ 48); if(s)x = -x; } int main() { // freopen("hdu6162.in", "r", stdin); int a, b; while(cin >> n >> q) { pst.init(); init(); for(int i = 1; i <= n; ++i)read(v[i]), dv[++dvcnt] = v[i]; for(int i = 1; i < n; ++i) { read(a), read(b); add(a, b); add(b, a); } for(int i = 1; i <= q; ++i) { read(qs[i].ql), read(qs[i].qr), read(qs[i].a), read(qs[i].b); dv[++dvcnt] = qs[i].a; dv[++dvcnt] = qs[i].b; } prepare4lca(); sort(dv + 1, dv + dvcnt + 1); dvcnt = unique(dv + 1, dv + dvcnt + 1) - dv; buildPST(1, 0); for(int i = 1; i <= q; ++i) { int s = qs[i].ql, t = qs[i].qr; int ilca = lca(s, t); LL sum = pst.query(ilca, s, indexOf(qs[i].a), indexOf(qs[i].b), 1) + pst.query(ilca, t, indexOf(qs[i].a), indexOf(qs[i].b), 0); cout << sum << (i == q ? ' ' : ' '); } } return 0; }
E FFF at Valentine (DFS)
H Numbers
J Two strings (记忆化搜索 / dp / 正则表达式 )
题意:给一个s串,由大小写字母组成,给一个t串,由大小写字母和' . '和' * '组成,其中点可以替换任何字符,星可以改变前面那个字符的任意长度(0到正无穷),能否匹配s和t?
思路:记忆化搜索
1.dfs(i,j)表示s[i]和t[j]匹配
那么对于t[j],分3种情况
1.1 : t[j]为' . '那么,我们可以dfs(i+1,j+1),表示i和j匹配成功,那么用掉i和j之后,比较(i+1,j+1),同时,注意在dfs下去之前,把t[j]修改成s[i],在dfs结束之后修改回' . '
1.2 : t[j]为星,首先可以跳过这个星串,直接dfs(i,j+2)。
接下来,我们只需要:
看i是否和j-1匹配,如果匹配,dfs(i+1,j),表示*保留着继续搜,dfs(i+1,j+1),表示*不保留了。
1.3 : t[j]== s[i] 直接往下搜便可 dfs(i+1,j+1)
如果t[j+1]是星,那么我们还可以dfs一次dfs(i,j+2)
判断可行的标准有:
i和j都超出了s串和t串的长度
i超出长度,j刚好在t串尾部,且尾部为星
i超出长度,j在尾部-1的位置,且尾部为星
由于每种情况搜了一次就不再继续搜,所以复杂度是O(2500*2500*T)
#include <bits/stdc++.h> using namespace std; string s,t; int ok; bool vis[2600][2600]; int dfs(int i,int j){ if(vis[i][j])return vis[i][j]; if(i==s.size()&&j==t.size()){ ok=1; return vis[i][j]=1; } if(i==s.size()&&j==t.size()-1&&t[j]=='*'){ ok=1; return vis[i][j]=1; } if(i==s.size()&&j==t.size()-2&&t[j+1]=='*'){ ok=1; return vis[i][j]=1; } if(ok){ return vis[i][j]=1; } if(i==s.size())return vis[i][j]=-1; if(t[j]=='.'){ t[j]=s[i]; vis[i+1][j+1]=dfs(i+1,j+1); t[j]='.'; if(j+1<t.size() && t[j+1]=='*'){ vis[i][j+2]=dfs(i,j+2); } } else if(t[j]=='*'){ vis[i][j+1]=dfs(i,j+1); if(t[j-1]=='.'){ vis[i+1][j]=dfs(i+1,j); vis[i+1][j+1]=dfs(i+1,j+1); }else{ if(s[i]==t[j-1]){ vis[i+1][j+1]=dfs(i+1,j+1); vis[i+1][j]=dfs(i+1,j); } } } else { if(s[i]==t[j]){ vis[i+1][j+1]=dfs(i+1,j+1); } if(j+1<t.size() && t[j+1]=='*'){ vis[i][j+2]=dfs(i,j+2); } } } int main(){ int T;cin>>T; while(T--){ memset(vis,0,sizeof vis); cin>>s>>t; ok=0; dfs(0,0); printf("%s ",ok ? "yes":"no"); } return 0; }