『模拟赛』冲刺CSP联训模拟2
Rank 不重要了
- Upd on 10.5:更新 T3 详细分讨。
A. 挤压
你说的对,期望怎么能算签呢?
一个重要的性质:一个数的平方可以在二进制下表示为 \(\sum_{i,j}\ s_i\ s_j\ 2^{i+j}\),所以就可以分别求每一位对答案的贡献了。
设 \(f_{i,1/0,1/0}\) 表示到第 \(i\) 个数我们枚举的两位分别是 1/0 的方案数,简单 dp 一下即可,复杂度 \(\mathcal{O(n\log^2n)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 2e5 + 5;
const int mod = 1e9 + 7;
int n;
ll a[N], f[N][2][2], ans, p[N];
namespace Wisadel
{
ll Wqp(ll x, int y)
{
ll res = 1;
while(y){if(y & 1) res = res * x % mod; x = x * x % mod, y >>= 1;}
return res;
}
short main()
{
freopen("a.in", "r", stdin) , freopen("a.out", "w", stdout);
n = qr;
ll zc = Wqp(1000000000, mod - 2);
fo(i, 1, n) a[i] = qr;
fo(i, 1, n) p[i] = qr, p[i] = p[i] * zc % mod;
fo(i, 0, 29) fo(j, 0, 29)
{
f[0][0][0] = 1, f[0][0][1] = f[0][1][0] = f[0][1][1] = 0;
fo(k, 1, n) fo(x, 0, 1) fo(y, 0, 1)
{
f[k][x][y] = f[k - 1][x][y] * (1 + mod - p[k]) % mod;
bool A = a[k] & (1 << i), B = a[k] & (1 << j);
int aa = x ^ (A ? 1 : 0), bb = y ^ (B ? 1 : 0);
f[k][x][y] = (f[k][x][y] + f[k - 1][aa][bb] * p[k] % mod) % mod;
}
ans = (ans + (f[n][1][1] * ((1ll << (i + j)) % mod) % mod)) % mod;
}
printf("%lld\n", ans);
return Ratio;
}
}
int main(){return Wisadel::main();}
B. 工地难题
计数题,仙姑。
注意到恰好 \(k\) 个的条件并不好满足,所以简单容斥为每次求不超过 \(k\) 的方案数,那么减掉 \(k-1\) 的方案数即为答案。
设 \(f_i\) 表示在序列中钦定 \(i\) 个长度超过 \(k\) 的 1 连续段的方案数。考虑如何求 \(f_i\),可以看做是先从 \(n-m+1\) 个空隙中选出 \(i\) 个,然后让它们都先含有 \(k+1\) 个 1,然后对于剩下 \(m-i(k+1)\) 个 1 可以随意插到 \(n-m+1\) 个空隙中去,由插板法可得 \(f_i=\binom{n-m+1}{i}\binom{n-i(k+1)}{n-m}\)。
显然这样求会有很多重复的情况,简单容斥一下(?)可知答案为 \(\sum_{i\ge0}\ (-1)^i\times f_i\)。处理 \(f_i\) 时,由于只有 \(i\le \frac{m}{k+1}\) 时才会有贡献,所以复杂度是调和级数 \(\mathcal{O(n\ln n)}\) 的。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 2e5 + 5, M = 2e5;
const int mod = 1e9 + 7;
int n, m;
ll jc[N], ny[N], ans, las;
namespace Wisadel
{
ll Wqp(ll x, int y)
{
ll res = 1;
while(y){if(y & 1) res = res * x % mod; x = x * x % mod, y >>= 1;}
return res;
}
ll C(int x, int y)
{
return jc[x] * ny[y] % mod * ny[x - y] % mod;
}
short main()
{
freopen("a.in", "r", stdin) , freopen("a.out", "w", stdout);
n = qr, m = qr;
jc[0] = 1;
fo(i, 1, M) jc[i] = jc[i - 1] * i % mod;
ny[M] = Wqp(jc[M], mod - 2);
fu(i, M - 1, 0) ny[i] = ny[i + 1] * (i + 1) % mod;
fo(k, 1, m)
{
ll res = C(n, m);
fo(i, 1, n - m + 1)
{
if(i * (k + 1) > m) break;
res = (res + (i & 1 ? mod - 1 : 1) * C(n - i *(k + 1), n - m) % mod * C(n - m + 1, i)) % mod;
}
printf("%lld ", (res - las + mod) % mod), las = res;
}
return Ratio;
}
}
int main(){return Wisadel::main();}
C. 星空遗迹
- 我!T3!打!过!啦!
赛时只砸了两个小时,所以思路不很清晰,分讨漏了好多。
一眼线段树(羁绊 Wis'adel 的力量。首先将每个区间可能所处的状态列举出来:从最左赢到最右,从最右赢到最左,总中间一点赢到两边,整个区间是一种手势。注意到没有两边赢向中间的情况,是因为任意手势如果被两边手势赢,那么可以将它消去,所以两边赢向中间消去无用手势后就成了状态一、二或四。
考虑如何使两个任意状态的区间合并成这四种中的一种,发现中间相邻的两个手势的结果有影响,所以存下。之后根据不同的状态简单分讨合并即可。感谢 int_R 在之前那个超级线段树教会了我如何 pushup 和查询合并一起写,减少了我一半的码量。
其实分讨部分我好像写重了好多,不过无伤大雅。
分讨情况大概是 \(4\times 3\times 4 = 48\) 种,全列出来有点啰嗦,其实简单手模就能出来,你们想看评论下,我再补。
你们要的分讨
注:\(ln\) 和 \(rn\) 表示从左/右开始赢的长度,\(wlx\) 表示当前区间最终答案。区间状态是根据其 \(ln\) 与 \(rn\) 是否为 0 来判断的,简言之,\(ln and !rn\) 为状态 1,\(!ln and rn\) 为状态 2,\(ln and rn\) 为状态 3,\(!ln && !rn\) 为状态 4。
对于状态 4,左右区间的答案是一样的,为了方便合并到一起了。
按照博客里的情况分讨,一共以下 48 种情况:
为节约篇幅,省去了完全对称的情况,所以只写出了 \((4+3+2+1)\times 3\) 种。
左区间状态 1,右区间状态 1:
-
连接处左赢右:最终区间状态 1,长度 \(rt.ln = ls.ln + rs.ln +1\),答案同左区间。
-
连接处右赢左:最终区间状态 1,长度 \(rt.ln = ls.ln + rs.ln -1\),答案同左区间。
-
连接处平局:最终区间状态 1,长度 \(rt.ln = ls.ln + rs.ln\),答案同左区间。
左区间状态 1,右区间状态 2:
-
连接处左赢右:若 \(ls.ln + 1 > rs.rn\),则最终区间状态 1,长度 \(rt.ln = ls.ln - rs.rn + 1\),答案同左区间;否则最终区间状态 2 或 4,长度 \(rt.rn = rs.rn - ls.ln - 1\),答案同右区间。
-
连接处右赢左:同理,将上情况 1 前符号变换一下即可。
-
连接处平局:同理,去掉上情况中的 1 即可。
左区间状态 1,右区间状态 3:
-
连接处左赢右:若 \(ls.ln + 1 \ge rs.rn\),则最终区间状态 1,长度 \(rt.ln = ls.ln + 1 - rs.rn + rs.ln\),答案同左区间;否则最终区间状态 3,长度 \(rt.ln = rs.rn\),\(rt.rn = rs.rn - 1 - ls.ln\),答案同右区间。
-
连接处右赢左:同理,将上情况 1 前符号变换一下即可。
-
连接处平局:同理,去掉上情况中的 1 即可。
左区间状态 1,右区间状态 4:
-
连接处左赢右:最终区间状态 1,长度 \(rt.ln = ls.ln + 1\),答案同左区间。
-
连接处右赢左:最终区间状态 1 或 4,长度 \(rt.ln = ls.ln - 1\),答案同左区间。
-
连接处平局:同左赢右,去掉 1 即可。
左区间状态 2,右区间状态 2:
-
连接处左赢右:最终区间状态 2,长度 \(rt.rn = ls.rn + rs.rn - 1\),答案同右区间。
-
连接处右赢左:同理,将上情况 1 前符号变换一下即可。
-
连接处平局:同理,去掉上情况中的 1 即可。
左区间状态 2,右区间状态 3:
-
连接处左赢右:最终区间状态 3,长度 \(rt.ln = rs.rn\),\(rt.rn = ls.rn + rs.rn - 1\),答案同右区间。
-
连接处右赢左:同理,将上情况 1 前符号变换一下即可。
-
连接处平局:同理,去掉上情况中的 1 即可。
左区间状态 2,右区间状态 4:
-
连接处左赢右:最终区间状态 2 或 4,长度 \(rt.rn = ls.rn - 1\),答案同右区间。
-
连接处右赢左:最终区间状态 2,长度 \(rt.rn = ls.rn + 1\),答案同右区间。
-
连接处平局:同右赢左,去掉 1 即可。
左区间状态 3,右区间状态 3:
-
连接处左赢右:最终区间状态 3。若 \(ls.ln + 1 > rs.rn\),则长度 \(rt.ln = ls.ln - rs.rn + 1 + rs.ln\),\(rt.rn = ls.rn\),答案同左区间;否则长度 \(rt.ln = rs.ln\),\(rt.rn = rs.rn - ls.ln - 1 + ls.rn\),答案同右区间。
-
连接处右赢左:同理,将上情况 1 前符号变换一下即可。
-
连接处平局:同理,去掉上情况中的 1 即可。
左区间状态 3,右区间状态 4:
-
连接处左赢右:最终区间状态 3,长度 \(rt.ln = ls.ln + 1\),\(rt.rn = ls.rn\),答案同左区间。
-
连接处右赢左:最终区间状态 3 或 2,长度 \(rt.rn = ls.rn\),\(rt.ln = ln.ln - 1\),答案同左区间。
-
连接处平局:最终区间状态 3,长度、答案均同左区间。
左区间状态 4,右区间状态 4:
-
连接处左赢右:最终区间状态 1,长度 \(rt.ln = 1\),答案同左区间。
-
连接处右赢左:最终区间状态 2,长度 \(rt.rn = 1\),答案同右区间。
-
连接处平局:最终区间状态 4,答案同左区间。
手动分割代码。
点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 2e5 + 5, x = 1e9;
const int mod = 1e9 + 7;
int n, m;
string s;
int a[N];
struct rmm
{
int llx, rlx, wlx;
int ln, rn;
bool wudi;
rmm(){llx = rlx = wlx = ln = rn = 0; wudi = 0;}
} t[N << 2];
namespace Wisadel
{
int W(char c)
{
if(c == 'R') return 1;
else if(c == 'S') return 2;
else return 3;
}
char M(int x)
{
if(x == 1) return 'R';
else if(x == 2) return 'S';
else return 'P';
}
int Wpk(int aa, int bb)
{
if(!aa || !bb) return aa + bb;
if(aa == bb) return aa;
if(((aa == 1 || aa == 2) && bb == aa + 1) || (aa == 3 && bb == 1) ) return aa;
return bb;
}
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define mid ((l + r) >> 1)
rmm Wpushup(rmm a, rmm b)
{
rmm c;
c.llx = a.llx, c.rlx = b.rlx;
c.ln = c.rn = 0;
c.wudi = 0;
int wn = Wpk(a.rlx, b.llx);
if(a.wudi && b.wudi)
{
if(a.rlx != b.llx)
{
if(wn == a.rlx)//
{
if(a.ln + 1 > b.rn)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln + 1 - b.rn + b.ln;
}
else
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn - 1 - a.ln + a.rn;
}
}
else
{
if(a.ln > b.rn + 1)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln - 1 - b.rn + b.ln;
}
else
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn + 1 - a.ln + a.rn;
}
}
}
else
{
if(a.ln > b.rn)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln - b.rn + b.ln;
}
else
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn - a.ln + a.rn;
}
}
c.wudi = 1;
return c;
}
if(a.wudi)
{
if(a.rlx != b.llx)
{
if(wn == a.rlx)
{
if(b.ln)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln + b.ln + 1;
c.wudi = 1;
}
else if(b.rn)
{
if(a.ln + 1 > b.rn)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln + 1 - b.rn;
c.wudi = 1;
}
else
{
c.wlx = b.wlx;
c.rn = b.rn - a.ln - 1 + a.rn;
}
}
else
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln + 1;
c.wudi = 1;
}
}
else
{
if(b.ln)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln + b.ln - 1;
c.wudi = 1;
}
else if(b.rn)
{
if(a.ln - 1 > b.rn)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln - 1 - b.rn;
c.wudi = 1;
}
else
{
c.wlx = b.wlx;
c.rn = b.rn - a.ln + 1 + a.rn;
}
}
else
{
c.wlx = a.wlx;
c.rn = a.rn;
if(a.ln > 1)
{
c.ln = a.ln - 1;
c.wudi = 1;
}
}
}
}
else
{
if(b.ln)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln + b.ln;
c.wudi = 1;
}
else if(b.rn)
{
if(a.ln > b.rn)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = a.ln - b.rn;
c.wudi = 1;
}
else
{
c.wlx = b.wlx;
c.rn = b.rn - a.ln + a.rn;
}
}
else
{
c.wlx = a.wlx;
c.ln = a.ln, c.rn = a.rn;
c.wudi = 1;
}
}
return c;
}
if(b.wudi)
{
if(a.rlx != b.llx)
{
if(wn == b.llx)
{
if(a.rn)
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn + 1 + a.rn;
c.wudi = 1;
}
else if(a.ln)
{
if(b.rn + 1 > a.ln)
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn + 1 - a.ln;
c.wudi = 1;
}
else
{
c.wlx = a.wlx;
c.ln = a.ln - 1 - b.rn + b.ln;
}
}
else
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn + 1;
c.wudi = 1;
}
}
else
{
if(a.rn)
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn - 1 + a.rn;
c.wudi = 1;
}
else if(a.ln)
{
if(b.rn - 1 > a.ln)
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn - 1 - a.ln;
c.wudi = 1;
}
else
{
c.wlx = a.wlx;
c.ln = a.ln + 1 - b.rn + b.ln;
}
}
else
{
c.wlx = b.wlx;
c.ln = b.ln;
if(b.rn > 1)
{
c.rn = b.rn - 1;
c.wudi = 1;
}
}
}
}
else
{
if(a.rn)
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn + a.rn;
c.wudi = 1;
}
else if(a.ln)
{
if(b.rn > a.ln)
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn - a.ln;
c.wudi = 1;
}
else
{
c.wlx = a.wlx;
c.ln = a.ln - b.rn + b.ln;
}
}
else
{
c.wlx = b.wlx;
c.ln = b.ln, c.rn = b.rn;
c.wudi = 1;
}
}
return c;
}
//
if(a.rlx != b.llx)
{
if(wn == a.rlx)//
{
if((a.ln && b.ln) || (!a.ln && !a.rn && b.ln) || (a.ln && !b.ln && !b.rn))
{
c.wlx = a.wlx;
c.ln = a.ln + b.ln + 1;
}
else if(a.ln && b.rn)
{
if(a.ln > b.rn - 1)
{
c.wlx = a.wlx;
c.ln = a.ln - b.rn + 1;
}
else
{
c.wlx = b.wlx;
c.rn = b.rn - 1 - a.ln;
}
}
else if((a.rn && b.ln) || (a.rn && !b.ln && !b.rn))
{
c.wlx = a.wlx;
c.ln = b.ln + 1;
c.rn = a.rn;
c.wudi = 1;
}
else if(a.rn && b.rn)
{
c.wlx = b.wlx;
c.rn = a.rn + b.rn - 1;
}
else if(!a.ln && !a.rn && !b.ln && !b.rn)
{
c.wlx = a.wlx;
c.ln = 1;
}
else if(!a.ln && !a.rn && b.rn)
{
c.wlx = b.wlx;
c.rn = b.rn - 1;
}
}
if(wn == b.llx)// ←
{
// cout<<"BUOK!"<<endl;
if((a.rn && b.rn) || (!a.ln && !a.rn && b.rn) || (a.rn && !b.ln && !b.rn))
{
c.wlx = b.wlx;
c.rn = a.rn + b.rn + 1;
}
else if(a.ln && b.rn)
{
if(b.rn > a.ln - 1)
{
c.wlx = b.wlx;
c.rn = b.rn - a.ln + 1;
}
else
{
c.wlx = a.wlx;
c.ln = a.ln - 1 - b.rn;
}
}
else if((a.rn && b.ln) || (b.ln && !a.ln && !a.rn))
{
c.wlx = b.wlx;
c.ln = b.ln;
c.rn = a.rn + 1;
c.wudi = 1;
}
else if(a.ln && b.ln)
{
c.wlx = a.wlx;
c.ln = a.ln + b.ln - 1;
}
else if(!a.ln && !a.rn && !b.ln && !b.rn)
{
c.wlx = b.wlx;
c.rn = 1;
}
else if(a.ln && !b.ln && !b.rn)
{
// cout<<"BUOK!"<<endl;
c.wlx = a.wlx;
c.ln = a.ln - 1;
}
}
}
else
{
if((a.ln && b.ln) || (!a.ln && !a.rn && b.ln) || (a.ln && !b.ln && !b.rn))
{
c.wlx = a.wlx;
c.ln = a.ln + b.ln;
}
else if((a.ln && b.rn))
{
if(a.ln > b.rn)
{
c.wlx = a.wlx;
c.ln = a.ln - b.rn;
}
else
{
c.wlx = b.wlx;
c.rn = b.rn - a.ln;
}
}
else if(a.rn && b.ln)
{
c.wlx = a.wlx;
c.rn = a.rn, c.ln = b.ln;
c.wudi = 1;
}
else if((a.rn && b.rn) || (!a.ln && !a.rn && b.rn) || (a.rn && !b.ln && !b.rn))
{
c.wlx = b.wlx;
c.rn = a.rn + b.rn;
}
else if(!a.ln && !a.rn && !b.ln && !b.rn)
{
c.wlx = a.wlx;
}
}
return c;
}
void Wbuild(int rt, int l, int r)
{
if(l == r)
{
t[rt].llx = t[rt].rlx = t[rt].wlx = a[l];
t[rt].ln = t[rt].rn = 0;
return;
}
Wbuild(ls, l, mid), Wbuild(rs, mid + 1, r);
t[rt] = Wpushup(t[ls], t[rs]);
// cout<<l<<' '<<r<<' '<<t[rt].ln<<' '<<t[rt].rn<<' '<<t[rt].wlx<<' '<<t[rt].wudi<<endl;
}
void Wupd(int rt, int l, int r, int x, int k)
{
if(l == r)
{
t[rt].llx = t[rt].rlx = t[rt].wlx = k;
t[rt].ln = t[rt].rn = 0;
return;
}
if(x <= mid) Wupd(ls, l, mid, x, k);
else Wupd(rs, mid + 1, r, x, k);
t[rt] = Wpushup(t[ls], t[rs]);
}
rmm Wq(int rt, int l, int r, int x, int y)
{
if(x <= l && r <= y) return t[rt];
rmm lans, rans; int ly = 0, ry = 0;
if(x <= mid) lans = Wq(ls, l, mid, x, y), ly = 1;
if(y > mid) rans = Wq(rs, mid + 1, r, x, y), ry = 1;
if(ly && ry)
{
// cout<<l<<' '<<mid<<' '<<r<<endl;
// cout<<lans.ln<<' '<<lans.rn<<' '<<lans.wlx<<' '<<lans.wudi<<endl;
// cout<<rans.ln<<' '<<rans.rn<<' '<<rans.wlx<<' '<<rans.wudi<<endl;
// cout<<endl;
return Wpushup(lans, rans);
}
else if(ly) return lans;
else return rans;
}
short main()
{
// freopen(".in", "r", stdin) , freopen(".out", "w", stdout);
freopen("a.in", "r", stdin) , freopen("a.out", "w", stdout);
n = qr, m = qr;
cin>>s;
fo(i, 0, n - 1) a[i + 1] = W(s[i]);
Wbuild(1, 1, n);
fo(i, 1, m)
{
int op = qr, l = qr, r; char ch;
if(op == 1)
{
scanf(" %c", &ch);
Wupd(1, 1, n, l, W(ch));
}
else
{
r = qr;
// fo(j, l, r)
// {
// cout<<M(Wq(1, 1, n, j, j).wlx);
// }
// cout<<endl;
rmm zc = Wq(1, 1, n, l, r);
// cout<<zc.ln<<' '<<zc.rn<<' '<<zc.llx<<' '<<zc.rlx<<' ';
printf("%c\n", M(zc.wlx));
}
}
return Ratio;
}
}
int main(){return Wisadel::main();}
D. 纽带
还没看题。。
末
一开题就感到满满压迫感,三道 \(1e9+7\) 一道不很简单的数据结构。
看到期望和计数直接就似了,不过还是被 T1 控了 1h,有点亏感觉,T3 只打了 2h 多,应该多留出来硬拼的。
以后模拟赛也注意吧,遇到一眼不会的题就暴力略过,后面时间余出来了再尝试正解,不然遇到 T3 这种只是时间不够就太伤了。
不过好在最后改出来了,赢!