CSP模拟2
A. F
考场没想出来,寄。
既然 \(x\) 需要满足所有的 \(a\) 和 \(b\) 成立,所以我们把 \(a_1\) 与 \(b\) 数组异或一遍就可以直接得到所有 \(x\) 的预备役。
可能符合条件的 \(x\) 最多也只有 \(n\) 个,那就可以直接把剩下的 \(a\) 与 \(x\) 进行异或,看 \(b\) 中是否存在。
判存在这个用哈希、\(\text{map}\) 都可以,\(n\) 范围到 \(2000\),\(O(n^2 \log n)\) 可过。
Code:
// csp-2 A
#include <algorithm>
#include <iostream>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstdlib>
#include <map>
using namespace std;
const int N = 2050;
int n;
int a[N],b[N],x[N];
int ans[N],cnt;
map<int,bool> t;
int main() {
freopen("f.in","r",stdin);
freopen("f.out","w",stdout);
scanf("%d",&n);
for(int i = 1;i <= n; i++)
scanf("%d",&a[i]);
for(int i = 1;i <= n; i++) {
scanf("%d",&b[i]);
t[b[i]] = 1;
}
for(int i = 1;i <= n; i++)
x[i] = a[1] ^ b[i];
bool flag = 0;
for(int i = 1;i <= n; i++) {
flag = 0;
for(int j = 2;j <= n; j++) {
if(!t[a[j] ^ x[i]]) {
flag = 1;
break;
}
}
if(!flag)
ans[++cnt] = x[i];
}
sort(ans + 1,ans + cnt + 1);
printf("%d\n",cnt);
for(int i = 1;i <= cnt; i++)
printf("%d\n",ans[i]);
fclose(stdin);
fclose(stdout);
return 0;
}
B.S
咕了 \(\dots\)
C.Y
DP 做法。
设 \(x_i\) 为第 \(i\) 个人向下一个人传的球数,\(x_i\) 与 \(b_i\) 间存在一个映射关系。
尽管每个人给别人的球的数量不同,但最终的序列 \(b\) 可能相同,例如每个人给别人 \(1\) 个球和 \(2\) 个球之后,每个人的球的数量是一样的,这就有了重复的情况。
所以由序列 \(x\) 可以得到唯一的序列 \(b\),而反之不成立。
可以发现,每个 \(x_i\) 都是正数所以我们把所有 \(x_i\) 都减去 \(1\) 后对应的 \(b_i\) 相同,因此,至少有一个人不传球的方案是与 \(b_i\) 存在双射的,即二者可以互相唯一得到。
思考 \(\prod b_i\) 的组合意义,在每个最终状态中,从每个人手中选出一个球的方案数。
最终状态下每个人的球有两种来源,一是自己原来就有的,二是上一个人传给她的。
设 \(dp_{i,0}\) 表示第 \(i\) 个人选她自己本来有的球的情况下,前 \(i - 1\) 个人选球的方案数;\(dp_{i,1}\) 表示第 \(i\) 个人选上一个人传给她的球的情况下,前 \(i\) 个人选球的方案数。
为什么 \(dp_{i,0}\) 要表示前 \(i - 1\) 个人的选球方案数呢?因为第 \(i\) 个人选自己本来有的情况下,会与下一个人产生关联。
如何转移?
第 \(i+1\) 个人如果选她自己本来有的球,其前 \(i - 1\) 个人方案数分两种情况求和计算。
若第 \(i\) 个人选自己本来有的,她可能选 \(1,2,3,\dots,a_i\) 个,根据乘法原理和加法原理,方案数为 \(dp_{i,0} \cdot \sum^{a_i}_{k = 1}k\)。
若第 \(i\) 个人选择上一个人传给她的球,她可以不选球,也可以选 \(1,2,3,\dots,a_i\) 个,所以方案数为 \(dp_{i,1} \cdot (a_i + 1)\)。
第 \(i\) 个人如果选择她自己本来有的球,她需要考虑给第 \(i\) 个人多少球,有 \(a_i + 1\) 种选择,给完球之后,第 \(i + 1\) 给人要从中选一个,第 \(i\) 个人从自己剩下的球里选一个。
但这个 \(\sum_{k = 1}^{a_i}k(a_i - k)\) 还可以再转化一下。
第 \(i\) 个人如果选择上一个人传给她的球,她需要考虑给第 \(i + 1\) 个人多少球,第 \(i + 1\) 个人还要在这些球中选一个。
最后再进行简单容斥,处理之前要求的符合双射的情况。
Code:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#define int long long
using namespace std;
const int N = 1919810;
const int MOD = MOD;
int n,a[N];
int Pow(int x) {
int y = MOD - 2;
int ans = 1;
while(y) {
if(y & 1)
ans = 1ll * ans * x % MOD;
x = 1ll * x * x % MOD;
y >>= 1;
}
return ans;
}
int dp[N][5];
int Inv_2 = Pow(2),Inv_6 = Pow(6);
int Sum(int x) {
return x * (x + 1) % MOD * Inv_2 % MOD;
}
int Get(int x) {
return x * (x + 1) % MOD * (2 * x + 1) % MOD * Inv_6 % MOD;
}
int DP(int x,int y) {
memset(dp,0,sizeof(dp));
dp[1][0] = x ^ 1;
dp[1][1] = x;
for(int i = 2;i <= n; i++) {
dp[i][0] = (dp[i - 1][0] * Sum(a[i - 1] - y) + dp[i - 1][1] * (a[i - 1] - y + 1)) % MOD;
dp[i][1] = (dp[i - 1][0] * (a[i - 1] * Sum(a[i - 1]) % MOD - Get(a[i - 1])) % MOD + dp[i - 1][1] * Sum(a[i - 1])) % MOD;
}
int ans;
if(x == 0)
ans = (dp[n][1] * (a[n] - y + 1) + dp[n][0] * Sum(a[n] - y)) % MOD;
else
ans = (dp[n][1] * Sum(a[n]) + dp[n][0] * (a[n] * Sum(a[n]) % MOD - Get(a[n]))) % MOD;
return ans;
}
signed main() {
#ifndef LOCAL
freopen("y.in","r",stdin);
freopen("y.out","w",stdout);
#endif
cin >> n;
for(int i = 1;i <= n; i++)
cin >> a[i];
cout << ((DP(1,0) + DP(0,0) - DP(1,1) - DP(0,1)) % MOD + MOD) % MOD;
return 0;
}
D.O
先咕一咕 \(\dots\)
考场思路粘一下。
/*
实质上每次风吹的时候的更改
就是把该位置以及前面的最大值移动到该位置
m 次操作后
a[i] = max[x - m,x] 初始位置值
所以要维护一个静态区间最大值
这样就可以
O(1) 查询 m 次更改后的 a[i] 的值
然后问题就是求和
目前为止其实就是 l == r 的思路
因为 l == r 相当于单点查询
直接 n log n 预处理最大值之后
就可以 O(1) 查询
每个点只能被前面的点更新
也就是在一定次数更新后
一些点的值就不会再更改了
这个时候可以计算无法更改部分的前缀和
似乎没有太大的优化
对于还没有完全更新完的点
它在某一次更新后的值相当于它在那一次扩展区间的最大值
增加了最大值 - 当前值
可以 O(1) 计算增加值
就可以 O(1) 计算单点对前缀和的影响值
每次更新 r 以前的前缀和 ……
复杂度仍然高 ……
想想线段树
需要支持区间推平和区间求和
复杂度都是 O(log n)
不能直接推
直接推每一次复杂度近似 O(n log n)
悲……
怎么做?!!!!
放弃
回去调 T3
*/