冲刺省选3月12日第三十场
冲刺省选3月12日第三十场
听歌听迷糊了
题目背景
风影剑醉:
今天地理都讲啥了啊
EAM:
反正没什么成本
EAM:
今天周六
风影剑醉:
哦
风影剑醉:
我过迷糊了
风影剑醉:
[对不起]
EAM:
[动画表情]
风影剑醉:
现在有点迷糊
EAM:
[嗨起来]
EAM:
唱会歌啥的
风影剑醉:
我唱歌五音不全
EAM:
或者跳个舞
风影剑醉:
我倒是一直听歌
EAM:
[跳舞]
EAM:
[动画表情]
EAM:
[动画表情]
EAM:
[动画表情]
EAM:
[动画表情]
EAM:
[动画表情]
EAM:
[动画表情]
风影剑醉:
你会唱歌嘛
EAM:
不会(๑ó﹏ò๑)
EAM:
我倒是一直听歌
风影剑醉:
这话我听过
EAM:
这话我也听过
风影剑醉:
你喜欢听啥歌啊
风影剑醉:
就?流行的
EAM:
对
EAM:
基本都是
风影剑醉:
我也差不多吧
EAM:
[听歌]
风影剑醉:
风影剑醉:
你怎么什么都有表情包
EAM:
还挺像
风影剑醉:
好家伙
风影剑醉:
[懊恼]
EAM:
[哈]
风影剑醉:
[讨厌]
EAM:
[略略]
题目
题目描述
我们定义\(f(a_1,a_2,\cdots,a_n)\)为\(\{a_1,a_2,\cdots,a_n\}\)的所有子序列异或和的总和。
比如\(f(1,2,3)=0+1+2+3+(1\oplus2)+(1\oplus3)+(2\oplus3)+(1\oplus2\oplus3)=12\)。
求所有满足\(l\leq a_i\leq r\)的\(f(a_1,a_2,\cdots,a_n)\)共有多少种不同的取值。
输入格式
本题包含多组测试数据。第一行一个整数\(T\)表示数据组数。
接下来\(T\)行,每行三个整数\(n,l,r\),表示一次询问。
输出格式
共\(T\)行,每行一个整数表示该组询问的答案。
样例输入
3
3 3 4
2 10 11
3 1 3
样例输出
3
2
3
数据范围
对于\(10\%\)的数据,\(T\leq 5,n\leq 5,r\leq 5\)。
对于\(30\%\)的数据,\(T\leq 50,n\leq 30,r\leq 100\)。
对于\(50\%\)的数据,\(n\leq 100,l,r\leq 10^5\)。
对于另外\(10\%\)的数据,\(n=1\)。
对于\(100\%\)的数据,有\(T\leq10^5,n\leq 100,1\leq l\leq r\leq10^{18}\)。
时间限制:1s
空间限制:256MB
AC代码
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
namespace qwq {
mt19937 eng;
void init(int Seed) {
eng.seed(Seed);
return;
}
int rnd(int l = 1, int r = 1000000000) {
return uniform_int_distribution<int> (l, r)(eng);
}
}
template <typename T>
inline T min(const T &x, const T &y) {
return x < y ? x : y;
}
template<typename T>
inline T max(const T &x, const T &y) {
return x > y ? x : y;
}
template <typename T>
inline T read() {
T x = 0;
bool f = 0;
char ch = getchar();
while (!isdigit(ch)) {
f = ch == '-';
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) {
if (y & 1) {
ret = (long long) ret * x % MOD;
}
}
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) {
return x - MOD;
}
else if (x < 0) {
return x + MOD;
}
else {
return x;
}
};
inline int ls(int k) {
return k << 1;
}
inline int rs(int k) {
return k << 1 | 1;
}
long long solve(long long l, long long r) {
if (l == r) {
return 1;
}
else {
long long x = l ^ r;
x = (1ULL << (64 - __builtin_clzll(x))) - 1;
l &= x;
r &= x;
long long ans = 0, m = 1ULL << (63 - __builtin_clzll(r)), l1 = m + l;
// 对于选择都是 0 的,那么很ok啊,就是 [l,2^id)的数字
ans = m - l;
long long y = r ^ m;
if (y) {
// 找到 r 的次大 1
// 那么这个后缀,都可以很简单的被取到是吧
y = (1ULL << (64 - __builtin_clzll(y))) - 1;
}
long long r1 = y | m;
if (r1 < l1) {
// 如果不能随便取,那就是从 r1 到这个 l
ans += r1 - l + 1;
}
else {
// 这表示从 [m, 2 * m - 1] 都能取了
ans += m;
}
return ans;
}
}
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
std::ios::sync_with_stdio(0);
cout << std::fixed << std::setprecision(8);
cin.tie(0);
cout.tie(0);
qwq::init(20050112);
// cout << (-3 / 2);
int Q, N;
long long l, r;
cin >> Q;
for (; Q--; ) {
cin >> N >> l >> r;
if (N == 1) {
cout << r - l + 1 << '\n';
}
else {
cout << solve(l, r) << '\n';
}
}
return (0-0);
}
还是挺神奇的一道题目,首先我们考虑,对于一个确定的序列,他们的异或和的和是啥。
如果这一位里有一个1了,那么考虑选不选这个东西,那么这一位必定有一半的可能是 \(0\),一半可能是 \(1\)。
那么,一个序列的答案就应该是 \((|^{n}_{i=1}a_i)\times 2 ^ {n-1}\)。
然后我们就考虑,其实就是问,有 \(n\) 个 \([l,r]\) 之间的数字,然后有多少种不同的或。
这个东西,好像是个经典题。
其实,可以发现,如果 \(n\ge 2\) 的话,其实就和 \(n=2\) 一样的。这个感性理解一下,你发现他很对。
我们考虑找到最大的 \(l\) 和 \(r\) 不一样的位 \(id\)。然后考虑,首先 \([l,2^{id}-1)\) 这些数字,肯定可以取到是吧。
然后考虑大于等于 \(2^{id}\) 的数字能取那些?
我们就可以设 \(l'\) 表示将 \(l\) 的 \(id\) 位改成 \(1\) 的。很明显,如果我们找到 \(r\) 的 \(id\) 之后的第一个 \(1\),那么,这个一后面的后缀一串 \(1\) 都可以取到设为 \(r1\)。
然后,对于如果我们选了 \([l,2^{id})\) 里的数字,如果 \(l+2^{id}<r\) 的后缀,那么就可以都取到,否则应该有可以取到 \(r1-l+1\) 个数字。
地理考试炸了
题目背景
EAM:
你的重心放计算机上就ok
风影剑醉:
[嗯嗯]
EAM:
[吃]
题目
题目描述
定义一棵无根树是三度树,当且仅当它的非叶节点的度数都是三。可以发现,具有\(n\)个叶子节点的一棵三度树恰好有 \(2n-3\) 条边。
考虑四个叶子\(A, B, C, D\),他们能构成三度树 \(T\) 的“四元划分”\(AB|CD\)当且仅当存在一条边,使得删去该边后可以分成两棵子树\(T_1, T_2\)满足\(A \in T_1, B \in T_1, C \in T_2, D \in T_2\) 。注意这里的定义是无序的,也就是说\(AB|CD, BA|CD, DC|AB\)视为是相同的;而 \(AB|CD\) 和 \(AC|BD\) 不相同。令\(Q(T)\)表示\(T\)的所有四元划分的集合。
给定两棵具有\(n\)个叶子节点的三度树\(T_A,T_B\),叶子节点标号均为\(1\)至\(n\),非叶节点标号均为\(n+1\)至\(2n-2\)。请求出\(|Q(T_A) \Delta Q(T_B)|\) 。在这里, \(A \Delta B\) 表示集合 \(A\) 和集合 \(B\) 的对称差。
输入格式
第一行一个正整数\(n\),表示叶结点数量。
接下来\(2n-3\)行,每行两个正整数\(x,y\),表示第一棵树的一条边。
再接下来\(2n-3\)行,每行两个正整数\(x,y\),表示第二棵树的一条边。
输出格式
一行一个整数,表示答案。
样例输入
5
1 7
2 6
3 6
4 8
5 8
6 7
7 8
1 6
2 7
3 6
4 8
5 8
6 7
7 8
样例输出
4
数据范围
对于\(10\%\)的数据, \(n \leq 20\) 。
对于\(20\%\)的数据, \(n \leq 100\) 。
对于\(40\%\)的数据, \(n \leq 300\) 。
对于\(100\%\)的数据,\(4 \leq n \leq 1000\) 。
时间限制:2s
空间限制:512MB
AC代码
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
namespace qwq {
mt19937 eng;
void init(int Seed) {
eng.seed(Seed);
return;
}
int rnd(int l = 1, int r = 1000000000) {
return uniform_int_distribution<int> (l, r)(eng);
}
}
template <typename T>
inline T min(const T &x, const T &y) {
return x < y ? x : y;
}
template<typename T>
inline T max(const T &x, const T &y) {
return x > y ? x : y;
}
template <typename T>
inline T read() {
T x = 0;
bool f = 0;
char ch = getchar();
while (!isdigit(ch)) {
f = ch == '-';
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return f ? -x : x;
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) {
if (y & 1) {
ret = (long long) ret * x % MOD;
}
}
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) {
return x - MOD;
}
else if (x < 0) {
return x + MOD;
}
else {
return x;
}
};
inline int ls(int k) {
return k << 1;
}
inline int rs(int k) {
return k << 1 | 1;
}
int N, vis[MAXN];
long long ans;
// 组合数
long long C(int x, int y) {
long long ret = 1;
for (int i = x - y + 1; i <= x; ++i) {
ret *= i;
}
for (int i = 1; i <= y; ++i) {
ret /= i;
}
return ret;
}
// 学到了一种结构体的写法,很妙
struct Graph {
vector<int> e[MAXN];
void add_edge(int x, int y) {
e[x].push_back(y);
e[y].push_back(x);
}
};
// 两棵树
struct Tree2 : Graph {
int sz[MAXN][4];
void getsz(int u, int fa) {
sz[u][1] = sz[u][2] = sz[u][3] = 0;
if (u <= N && vis[u]) {
sz[u][vis[u]] = 1;
}
for (auto &i: e[u]) {
if (i != fa) {
getsz(i, u);
for (int j = 1; j <= 3; ++j) {
sz[u][j] += sz[i][j];
}
}
}
return;
}
long long getans(int u, int fa) {
long long ret = 0, sum = 0;
for (auto &i: e[u]) {
for (auto &j: e[u]) {
if (i < j) {
for (int k = 1; k <= 3; ++k) {
for (int l = 1; l <= 3; ++l) {
if (k != l) {
ret += C((i != fa) ? sz[i][k] : (sz[N + 1][k] - sz[u][k]), 2) * C((j != fa) ? sz[j][l] : (sz[N + 1][l] - sz[u][l]), 2);
}
}
}
}
}
}
if (fa) {
// 这条边
for (int i = 1; i <= 3; ++i) {
for (int j = 1; j <= 3; ++j) {
if (i != j) {
ret -= C(sz[u][i], 2) * C(sz[N + 1][j] - sz[u][j], 2);
}
}
}
}
for (auto &i: e[u]) {
if (i != fa) {
ret += getans(i, u);
}
}
return ret;
}
} T2;
struct Tree1 : Graph {
void draw(int u, int fa, int col) {
vis[u] = col;
for (auto &i: e[u]) {
if (i != fa) {
draw(i, u, col);
}
}
return;
}
void getans(int u, int fa) {
int cnt = 0;
// O(ans);
memset(vis, 0, sizeof vis);
for (auto &i: e[u]) {
draw(i, u, ++cnt);
}
T2.getsz(N + 1, 0);
ans += T2.getans(N + 1, 0);
// O(ans);
if (fa) {
memset(vis, 0, sizeof vis);
draw(u, fa, 1);
draw(fa, u, 2);
T2.getsz(N + 1, 0);
ans -= T2.getans(N + 1, 0);
}
for (auto &i: e[u]) {
if (i != fa) {
getans(i, u);
}
}
return;
}
} T1;
int main() {
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
std::ios::sync_with_stdio(0);
cout << std::fixed << std::setprecision(8);
cin.tie(0);
cout.tie(0);
qwq::init(20050112);
cin >> N;
for (int i = 1, x, y; i < 2 * N - 2; ++i) {
cin >> x >> y;
T1.add_edge(x, y);
}
for (int i = 1, x, y; i < 2 * N - 2; ++i) {
cin >> x >> y;
T2.add_edge(x, y);
}
T1.getans(N + 1, 0);
ans = 2 * C(N, 4) - 2 * ans;
cout << ans << '\n';
// cout << (-3 / 2);
return (0-0);
}
就是很牛逼的,我们可以考虑这个对称差就是两个全集减去这个什么傻逼交集。
全集自然就是 \(\binom {N}{4}\) 因为我们选取四个顶点,肯定有一种合法方案(因为度数小于 4)。
然后考虑交集怎么求。
很难搞。
我们是不是可以这么搞,对于一棵树来说,一个四元划分 \(Q(AB|CD)\) 合法,那么肯定是说,\(AB\) 和 \(CD\) 这条链没有交,那么考虑他们之间的那条链。肯定满足边-点=1。于是可以考虑经典的容斥,每次考虑去掉一个边还是点。然后这样就可以求一个树时候的答案。
我们考虑怎么做两个树的交集?我们首先枚举第一棵树去掉的边或者点。这样这些点被划分成了几个连通块。然后对于二树上的每种连通块的点的容斥可以单独做。
这样就做完了。