BUPT 2021 Winter Training #6
作者:互联网
C - Even Path
水题略
F - Regular Forestation
题意
给一棵树,求出一点使得删去这点后剩下的两棵以上的树两两同构,且剩下树的数量尽可能多
思路
只有重心可能为good cutting point,求出重心再进行检验即可。
(有两个重心时不存在good cutting point)
关于无根树同构
由于树可能有两个重心,而且重心可能不对称,因此判断无根树同构的时候要分别尝试两个重心。采用树哈希方法的时候要分别以两个重心为根算出有根树哈希值,再以某种方法合并。这里一开始在算以第二个重心为根的哈希的时候没有更新把size更新成以第二个重心为根。后来发现了不用size的哈希算法,简化了代码。
代码
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
struct Edge
{
int v, next;
} e[8010];
int head[4010], cnt;
int sz[4010], tot;
int gcenter1, gcenter2, mn;
int ans;
void addedge(int u, int v)
{
cnt++;
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void dfs(int u, int f)
{
int mx = 0;
sz[u] = 1;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if (v == f || v == ans) continue;
dfs(v, u);
sz[u] += sz[v];
mx = max(mx, sz[v]);
}
mx = max(mx, tot - sz[u]);
if (mx < mn)
{
gcenter1 = u;
gcenter2 = 0;
mn = mx;
}
else if (mx == mn)
{
gcenter2 = u;
}
}
ull rooted_hash(int u, int f)
{
vector<ull> sub_h;
for (int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if (v == f || v == ans) continue;
sub_h.push_back(rooted_hash(v, u));
}
sort(sub_h.begin(), sub_h.end());
ull h = 1;
for (ull i : sub_h) h = (h * 7) ^ i;
return h;
}
ull rootless_hash(int u, int totsz)
{
mn = tot = totsz;
dfs(u, 0);
return rooted_hash(gcenter1, 0) + (gcenter2 ? rooted_hash(gcenter2, 0) : 0);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
int mxcnt = -1;
mn = tot = n;
dfs(1, 0);
ans = gcenter1;
dfs(ans, 0);
int subsz = sz[e[head[ans]].v];
ull h = rootless_hash(e[head[ans]].v, subsz);
int count = 1;
for (int i = e[head[ans]].next; i; i = e[i].next)
{
int v = e[i].v;
count++;
if (sz[v] != subsz || h != rootless_hash(v, sz[v]))
{
printf("-1\n");
return 0;
}
}
if (count == 1)
printf("-1\n");
else
printf("%d\n", count);
return 0;
}
K - Addition Robot
题意
给定一个由字符A或B构成的序列,进行两种操作:
- 反转指定区间内的字符
- 给定数值A、B以及一段区间,根据区间内的字符依次计算,遇到字符A则A+=B,遇到字符B则B+=A,输出最终A、B的值
思路
建立线段树,每个节点维护一个2x2矩阵,题目中的运算过程相当于矩阵乘向量,左右两端操作序列合并相当于右端对应矩阵乘左端对应矩阵,反转则相当于让矩阵旋转180°
代码
#include <bits/stdc++.h>
#define lc (u << 1)
#define rc (u << 1 | 1)
using namespace std;
typedef long long ll;
const int mod = 1000000007;
struct Mat
{
ll a, b, c, d;
Mat trans()
{
return (Mat){d, c, b, a};
}
};
Mat operator*(Mat lhs, Mat rhs)
{
return (Mat){(lhs.a * rhs.a + lhs.b * rhs.c) % mod, (lhs.a * rhs.b + lhs.b * rhs.d) % mod,
(lhs.c * rhs.a + lhs.d * rhs.c) % mod, (lhs.c * rhs.b + lhs.d * rhs.d) % mod};
}
Mat t[400010];
bool toggled[400010];
char a[100010];
void pushup(int u)
{
t[u] = t[rc] * t[lc];
}
void pushdown(int u)
{
if (toggled[u])
{
toggled[u] = false;
toggled[lc] = !toggled[lc];
toggled[rc] = !toggled[rc];
t[lc] = t[lc].trans();
t[rc] = t[rc].trans();
}
}
void build(int u, int l, int r)
{
if (l == r)
{
if (a[l] == 'A')
{
t[u] = (Mat){1, 1, 0, 1};
}
else
{
t[u] = (Mat){1, 0, 1, 1};
}
return;
}
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(u);
}
Mat query(int u, int l, int r, int ql, int qr)
{
if (ql <= l && qr >= r) return t[u];
pushdown(u);
int mid = (l + r) >> 1;
Mat ans = {1, 0, 0, 1};
if (qr >= mid + 1) ans = ans * query(rc, mid + 1, r, ql, qr);
if (ql <= mid) ans = ans * query(lc, l, mid, ql, qr);
return ans;
}
void toggle(int u, int l, int r, int ql, int qr)
{
if (ql <= l && qr >= r)
{
toggled[u] = !toggled[u];
t[u] = t[u].trans();
return;
}
pushdown(u);
int mid = (l + r) >> 1;
if (ql <= mid) toggle(lc, l, mid, ql, qr);
if (qr >= mid + 1) toggle(rc, mid + 1, r, ql, qr);
pushup(u);
}
int main()
{
int n, q;
scanf("%d%d%s", &n, &q, a + 1);
build(1, 1, n);
while (q--)
{
int op, l, r, a, b;
scanf("%d", &op);
if (op == 1)
{
scanf("%d%d", &l, &r);
toggle(1, 1, n, l, r);
}
else
{
scanf("%d%d%d%d", &l, &r, &a, &b);
Mat mt = query(1, 1, n, l, r);
ll na = (mt.a * a + mt.b * b) % mod;
ll nb = (mt.c * a + mt.d * b) % mod;
printf("%lld %lld\n", na, nb);
}
}
return 0;
}
标签:sz,Training,int,BUPT,mid,2021,ans,head,mx 来源: https://www.cnblogs.com/teralem/p/15847449.html