省选测试7
省选测试7
T1
刚开始想打暴力来着, 打着打着发现好像可以搞出正解.
题目大意就是两个点联通的时候可以看到对方发的信息, 其余时候不能.
那么对于一个点对\((x,y)\)称为好友的时候, 我们给\(x,y\)分别加上(-1 *对方总共发过的信息), 解除好友关系的时候给\(x,y\)分别加上对方总共发过的信息. 这样相当于就得到了他们联通时候的可以看到的信息条数.
注意我们还要加上最后一次操作之后哪些任然是联通关系的那些人的答案, 因为它们的答案并没有统计过. 用set记录一下有哪些点对是联通状态就好了.
\(O(m\log n)\)
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 2e5 + 5;
int n, m;
int ss[N], ans[N];
set <pair<int, int> > s;
void Work1() {
for(register int i = 1;i <= m; i++) {
char ch_; cin >> ch_;
int x, y;
if(ch_ == '!') {
x = read(); ss[x] ++;
}
if(ch_ == '+') {
x = read(); y = read();
if(x > y) swap(x, y);
s.insert(make_pair(x, y));
ans[x] -= ss[y]; ans[y] -= ss[x];
}
if(ch_ == '-') {
x = read(); y = read();
if(x > y) swap(x, y);
s.erase(make_pair(x, y));
ans[x] += ss[y]; ans[y] += ss[x];
}
}
set <pair<int, int> > :: iterator it;
it = s.begin();
while(it != s.end()) {
pair <int, int> tmp = *it; it ++;
ans[tmp.first] += ss[tmp.second];
ans[tmp.second] += ss[tmp.first];
}
for(int i = 1;i <= n; i++) printf("%d ", ans[i]);
}
int main() {
// freopen("qq.in","r",stdin); freopen("qq.out","w",stdout);
n = read(); m = read();
Work1();
// fclose(stdin); fclose(stdout);
return 0;
}
/*
2 8
! 1
! 2
+ 1 2
! 1
! 2
- 1 2
! 1
! 2
*/
T2
有一种字符串的加密方式 : 给出一段英文,我们把每一个单词翻转,并将其所有的大写字母都变为小写字母,最后在把这些单词收尾相接得到一个字符串,这样我们就加密完了。举个例子:\(ab AEs Ksd\)加密后变为\(baseadsk\)。
现在给出一个长度为\(n\)的加密后的字符串. 再给出\(m\)个可能在字符串中出现的单词.输出一种解密之后的句子.
\(n <= 10000, m <= 1000000,\)单词长度\(len <= 1000\), 保证单词的总长度不大于\(5000000\).
哈希.
我们可以暴力枚举出字符串每一个位置是哪些单次的结尾.因为单词长度不超过1000, 所以\(1000 * n\)还是可以枚举过来的, 判断当前单词是否可以放在这里, 用Hash + map\(O(1)\)判断就好了.
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, M = 1005, B = 233;
const unsigned long long mod = 998244353;
int n, m;
int g[N], pre[N];
char sl[N];
string ss[N];
unsigned long long h[N], hs[N], pow_B[N];
map <unsigned long long, int> mp;
unsigned long long Hash(string s) {
int len = s.length();
unsigned long long res = 0;
for(int i = len - 1;i >= 0; i--) {
int tmp = s[i] - 'a' + 1;
if(tmp < 0) tmp += 32;
res = (res * B % mod + tmp) % mod;
}
return res;
}
unsigned long long get_Hash_sl(int l, int r) {
return (hs[r] - hs[l - 1] * pow_B[r - l + 1] % mod + mod) % mod;
}
void Print(int x) {
if(!x) return ;
// cout << x << "\n";
Print(pre[x]);
int len = ss[g[x]].length();
for(int i = 0;i < len; i++) printf("%c", ss[g[x]][i]);
printf(" ");
}
int main() {
// freopen("char.in","r",stdin); freopen("char.out","w",stdout);
scanf("%d", &n);
for(int i = 1;i <= n; i++) cin >> sl[i];
for(int i = 1;i <= n; i++) hs[i] = (hs[i - 1] * B % mod + (sl[i] - 'a' + 1)) % mod;
pow_B[0] = 1; for(int i = 1;i <= n; i++) pow_B[i] = pow_B[i - 1] * B % mod;
scanf("%d", &m);
for(int i = 1;i <= m; i++) {
cin >> ss[i];
mp[Hash(ss[i])] = i;
}
g[0] = -1;
for(int i = 1;i <= n; i++) {
int cot = 0;
for(int j = i;j >= 1; j--) {
cot ++; if(cot > M - 5) break ;
if(!g[j - 1]) continue ;
unsigned long long res = get_Hash_sl(j, i);
if(mp[res]) {
g[i] = mp[res]; pre[i] = j - 1;
break ;
}
}
}
Print(n);
fclose(stdin); fclose(stdout);
return 0;
}
/*
30
ariksihsidlihcdnaehsetahgnisol
10
Kira
hates
is
he
losing
death
childish
L
and
Note
*/
T3
显然树剖.
线段树上维护这四个东西 :
\(f_0\)表示一些0从左往右走完后会变成什么东西; \(f_1\)表示一些1从左往右走完后会变成什么东西;
\(w_0\)表示一些0从右往左走完后会变成什么东西; \(w_1\)表示一些1从右往左走完后会变成什么东西;
因为我们要求的是原树上\(x\)到\(y\)的路径一些0走完时候会变成什么, 一些1走完后变成什么.这样才能从高位往低位贪心.(注意\(x\)到\(lca\)是从下往上走的, \(lca\)到\(y\)是从上往下走的.)
那么考虑线段树上怎么合并这四个东西呢?
假设当前先合并\(f_0\), 我们知道左子树的\(Af_0, Af_1\), 右子树的\(Bf_0, Bf_1\).
我们要求出一些0从左往右走完后会变成什么, 首先我们可以知道走完左子树会变成\(Af_0\), 有一些位会变成1, 有一些还是0. 举个例子 : 假设变成了1101001, 那么说明这些0第0,3,5,6位从左往右走完左子树变成了1;
然后我们考虑\(Af_0 \& Bf_1\)是什么东西, 是不是这些1从左往右走过右子树变成了什么.
那么\(!Af_0\&Bf_0\)是什么呢? 是不是这些0从左往右走过右子树会变成什么.因为我们有一个取反操作, 也就是把从左区间出来的那些0变成了1, 这样与(&)上又子树的\(Bf_0\),就相当于是得到了有那些0会变成1.最后把两个或(|)起来就是答案.
总结一下 : \(f_0 = (Af_0 \& Bf_1)|(!Af_0\&Bf_0)\).
其他也是同理 :
\(f_1 = (Af_1 \& Bf_1)|(!Af_1\&Bf_0)\),
\(w_0 = (Bw_0 \& Aw_1)|(!Bw_0\&Aw_0)\)
\(w_1 = (Bw_1 \& Aw_1)|(!Bw_1\&Af_0)\)
维护好了这四个东西后就可以愉快的树剖了, 但是有很多的细节需要注意 :
从下到上合并\(x\)到\(lca\)的答案时, 要注意把\((f_0, w_0), (f_1, w_1)\)交换过来, 因为原来的合并操作是\(w_0 = (Bw_0 \& Aw_1)|(!Bw_0\&Aw_0)\), 从下往上合并嘛, 自然地就会把下边那部分当做左子树去合并了, 这样合并出来的0是先经过上部分, 在经过下部分的, 就不对了.
注意所有的\(1\)都开成\(1ull\).
#include <bits/stdc++.h>
#define ull unsigned long long
#define rei register int
using namespace std;
ull read() {
ull s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 2e5 + 5;
int n, m, K;
ull t_64;
struct node { int opt; ull x; } a[N];
struct edge { int to, nxt; } e[N << 1];
int tot, cnt;
int fa[N], dep[N], dfn[N], siz[N], hav[N], top[N], fid[N], head[N];
void add(rei x, rei y) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
}
void get_tree(rei x, rei Fa) {
fa[x] = Fa; siz[x] = 1; dep[x] = dep[Fa] + 1;
for(rei i = head[x]; i ; i = e[i].nxt) {
rei y = e[i].to; if(y == Fa) continue ;
get_tree(y, x); siz[x] += siz[y];
if(siz[y] > siz[hav[x]]) hav[x] = y;
}
}
void get_top(rei x, rei topic) {
dfn[x] = ++ tot; fid[tot] = x; top[x] = topic;
if(hav[x]) get_top(hav[x], topic);
for(rei i = head[x]; i ; i = e[i].nxt) {
rei y = e[i].to; if(y == fa[x] || y == hav[x]) continue ;
get_top(y, y);
}
}
#define ls(o) (o << 1)
#define rs(o) (o << 1 | 1)
#define mid ((l + r) >> 1)
struct tree { ull f0, f1, w0, w1; } t[N << 2];
void up(rei o) {
t[o].f0 = (t[ls(o)].f0 & t[rs(o)].f1) | ((t[ls(o)].f0 ^ t_64) & t[rs(o)].f0);
t[o].f1 = (t[ls(o)].f1 & t[rs(o)].f1) | ((t[ls(o)].f1 ^ t_64) & t[rs(o)].f0);
t[o].w0 = (t[rs(o)].w0 & t[ls(o)].w1) | ((t[rs(o)].w0 ^ t_64) & t[ls(o)].w0);
t[o].w1 = (t[rs(o)].w1 & t[ls(o)].w1) | ((t[rs(o)].w1 ^ t_64) & t[ls(o)].w0);
}
void build(rei o, rei l, rei r) {
if(l == r) {
if(a[fid[l]].opt == 1) {
t[o].f0 = 0; t[o].f1 = a[fid[l]].x;
t[o].w0 = 0; t[o].w1 = a[fid[l]].x;
}
if(a[fid[l]].opt == 2) {
t[o].f0 = a[fid[l]].x; t[o].f1 = t_64;
t[o].w0 = a[fid[l]].x; t[o].w1 = t_64;
}
if(a[fid[l]].opt == 3) {
t[o].f0 = a[fid[l]].x ^ 0; t[o].f1 = a[fid[l]].x ^ t_64;
t[o].w0 = a[fid[l]].x ^ 0; t[o].w1 = a[fid[l]].x ^ t_64;
}
return ;
}
build(ls(o), l, mid); build(rs(o), mid + 1, r);
up(o);
}
void change(rei o, rei l, rei r, rei x) {
if(l == r) {
if(a[fid[l]].opt == 1) {
t[o].f0 = 0; t[o].f1 = a[fid[l]].x;
t[o].w0 = 0; t[o].w1 = a[fid[l]].x;
}
if(a[fid[l]].opt == 2) {
t[o].f0 = a[fid[l]].x; t[o].f1 = t_64;
t[o].w0 = a[fid[l]].x; t[o].w1 = t_64;
}
if(a[fid[l]].opt == 3) {
t[o].f0 = a[fid[l]].x ^ 0; t[o].f1 = a[fid[l]].x ^ t_64;
t[o].w0 = a[fid[l]].x ^ 0; t[o].w1 = a[fid[l]].x ^ t_64;
}
return ;
}
if(x <= mid) change(ls(o), l, mid, x);
if(x > mid) change(rs(o), mid + 1, r, x);
up(o);
}
tree merge(tree a, tree b) {
tree c;
c.f0 = (a.f0 & b.f1) | ((a.f0 ^ t_64) & b.f0);
c.f1 = (a.f1 & b.f1) | ((a.f1 ^ t_64) & b.f0);
c.w0 = (b.w0 & a.w1) | ((b.w0 ^ t_64) & a.w0);
c.w1 = (b.w1 & a.w1) | ((b.w1 ^ t_64) & a.w0);
return c;
}
tree query(int o, int l, int r, int x, int y) {
if(x <= l && y >= r) { return t[o]; }
if(y <= mid) return query(ls(o), l, mid, x, y);
else if(x > mid) return query(rs(o), mid + 1, r, x, y);
else return merge(query(ls(o), l, mid, x, y), query(rs(o), mid + 1, r, x, y));
}
// 1 : f0
// 2 : f1
// 3 : w0
// 4 : w1
int cnt1, cnt2;
tree ans1[N], ans2[N];
ull query_path(rei x, rei y, ull z) {
cnt1 = cnt2 = 0;
while(top[x] != top[y]) {
if(dep[top[x]] > dep[top[y]]) {
ans1[++ cnt1] = query(1, 1, n, dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
else {
ans2[++ cnt2] = query(1, 1, n, dfn[top[y]], dfn[y]);
y = fa[top[y]];
}
}
if(dep[x] > dep[y]) ans1[++ cnt1] = query(1, 1, n, dfn[y], dfn[x]);
else ans2[++ cnt2] = query(1, 1, n, dfn[x], dfn[y]);
tree res;
if(cnt1) {
for(int i = 1;i <= cnt1; i++) swap(ans1[i].f0, ans1[i].w0), swap(ans1[i].f1, ans1[i].w1);
res = ans1[1];
for(int i = 2;i <= cnt1; i++) res = merge(res, ans1[i]);
if(cnt2) for(int i = cnt2;i >= 1; i--) res = merge(res, ans2[i]);
}
else if(cnt2) {
res = ans2[cnt2];
for(int i = cnt2 - 1;i >= 1; i--) res = merge(res, ans2[i]);
}
ull ans = 0;
for(int i = 63;i >= 0; i--) {
rei tmp1 = (res.f0 >> i) & 1, tmp2 = (res.f1 >> i) & 1;
if(tmp1 == 1) ans |= (1ull << i);
else if(tmp2 == 1 && (1ull << i) <= z) ans |= (1ull << i), z -= (1ull << i);
}
return ans;
}
void Init() {
n = read(); m = read(); K = read();
t_64 = 1ull;
for(rei i = 1;i <= K; i++) t_64 = ((t_64 << 1ull) | 1ull);
for(rei i = 1;i <= n; i++) a[i].opt = read(), a[i].x = read();
for(rei i = 1, x, y;i < n; i++) {
x = read(); y = read(); add(x, y); add(y, x);
}
}
void Work() {
get_tree(1, 0); get_top(1, 1); build(1, 1, n);
for(rei i = 1, opt, x, y;i <= m; i++) {
opt = read(); x = read(); y = read();
ull z = read();
if(opt == 1) printf("%llu\n", query_path(x, y, z));
if(opt == 2) a[x].opt = y, a[x].x = z, change(1, 1, n, dfn[x]);
}
}
int main() {
// freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout);
Init(); Work();
// fclose(stdin); fclose(stdout);
return 0;
}
/*
1 1 5
3 10
1 1 1 3
5 5 3
1 7
2 6
3 7
3 6
3 1
1 2
2 3
3 4
1 5
1 1 4 7
1 1 3 5
2 1 1 3
2 3 3 3
1 1 3 2
*/