8.21-8.26
做题记录 8.21~8.26
补题list
AT_arc227_e [ARC227E] Shift and XOR Switches
考虑生成函数。
系数的奇偶性有多少种可能性。
不妨考虑一下 \(s_i\) 互不相同?
容易发现与系数也是双射的。
证明就考虑最低位的 \(1\),一定是在集合内的,用大除法除掉。
以此类推我们可以得到。
怎么把相同变成不同?
注意到两个 \(X\) 可以变成 \(2X\),以此类推就能得到互不相同的序列。
我们对它计数即可。
考虑一个 01串能否被构造出来?
从低位贪心,能选就选,其他的都配对到下一位去。
P17284 「IXOI R2」想不出来
给定长度为 \(n\) 的序列 \(x\)。
你有一个长度为 \(n\) 的序列 \(p\),初始 \(\forall i\in [1,n],p_i=1\)。
定义一次操作为:选择 \(i\in[1,n]\),将序列中 \(i-1\) 位置和 \(i+1\) 位置上的数移动到 \(i\) 位置上。
形式化地,每次操作可以视为,令:
- \(p_i\leftarrow p_i+p_{i-1}+p_{i+1}\);
- \(p_{i-1}\leftarrow 0\);
- \(p_{i+1}\leftarrow 0\)。
特别地,如果 \(i-1\) 或 \(i+1\) 位置不存在,则不在 \(i-1\) 或 \(i+1\) 位置进行操作。
我们称一个序列 \(p'\) 是愚蠢的,当且仅当它可以通过任意次操作由初始序列 \(p\) 生成。
我们称两个序列 \(a,b\) 是本质不同的,当且仅当 \(\exist i\in [1,n],a_i\neq b_i\)。
定义序列 \(p'\) 的权值为:
现在请求出所有本质不同的愚蠢的序列 \(p'\) 的权值和并输出答案对 \(10^9+7\) 取模后的结果。
做法
对于这种序列操作的问题,我们容易有个想法就是不研究如何构造。
而是转为研究最终序列的性质。
先找最终序列的必要条件,拼出一个充要条件。
注意:不要先对原序列转化,先对原序列做,不好做再转化。
我们先对原序列进行手玩。尝试找出一些必要条件。
\(0 \ x \ 0\) 这是操作中间的结果。
容易发现对于 \(x \ge 2\) 左右必然是 \(0\).这个是显然的。
其次,由于我们操作一次周围必然是 \(0\)。
那么对于两个 \(>0 \ >0\) 排列在一起,必然它们都没有被操作过。
因为 \(x\) 想要 \(>0\) 要么 \(x\) 不动,要么操作 \(x\),而必然带来隔壁的 \(0\)。
由第一条,我们可以得到,只有两个 \(1\) 才能相邻。
由第二条得到这两个 \(1\) 必然没有操作过。
即 \(\sum_{j\le i} p_j= i\).
这些是必要的吗?打表验证即可
然后dp。
https://codeforces.com/contest/2257/problem/F1
史题,但加深了对矩阵的理解。
大意是跳跃 \([1,x] m\),还有若干个平台,跳到同一个平台有罚时。
\(L \to R\) 最小罚时。\(x\le 10\)
做法
先考虑静态问题,这个是直接 dp。
由于用到的 dp 很少,我们可以记下来矩阵优化转移。
但平台很长,一个个乘用矩阵快速幂会爆。
这里有个理解是可以把矩阵当做一个转换器。
记 \(mat[i][j]\) 是一段连续 \(X\) 段到另一段连续 \(X\) 的段,\(i\to j\) 的最小代价。
做 \((min,+)\) 矩阵乘法即可。
这样我们可以处理出一段平台的转换器,因为会直接跳过去很好处理。
这样我们就做完了 easy ver.
hard ver. 很坑的一个点是时间并没有炸。
而空间被卡了。
我们考虑怎么能线段树不存那么多矩阵。
考虑分块优化线段树空间。
设 \(B\),线段树中有 \(\frac{n}{B}\) 个点。
单点修改直接改块加线段树单修,\(O(Bx^3+\log \frac{n}{B} \times x^3)\).
区间查同理。
调平一下 \(B = 16\).
空间复杂度 \(O(\frac{n}{B}x^2)\)。
比较变态的是建树有个固定的 $O(n x^3) $,大约是 1e9 级别。
但是由于小矩阵乘法的常数比较小,缓存比较友好,可以通过。
https://codeforces.com/problemset/problem/845/F
直接考虑轮廓线dp 即可。
P10592 BZOJ4361 isn
给出一个长度为 n 的序列 a1,a2,…a**n。如果序列 a 不是非降的,你必须从中删去一个数。
这一操作将被不断执行,直到 A 非降为止。求有多少种不同的操作方案。
做法
1
注意到在最后一次操作前,序列都是一个非递增的。
而操作完最后一次,序列彻底变成递增的。
因此容易发现除了最后一次前面可以任意操作。
考虑一个最终的递增序列 \(\{S\}\)。长度为 \(k\)。
设最后一次删除 \(x\),满足 \(x\) 加入一定破坏单调性。
方案数即为 \(\sum _{\{S\}}(n-k-1)! \times cnt_x\).
\(cnt_x\) 为可破坏 \(\{S\}\) 单调性的个数。
破坏单调性好像不太简单。
转成不破坏,就变成只要 \(s_i \le x \le s_{i+1}\) 即可。
\(\sum _{\{S\}}(n-k-1)! \times (n-k-cnt'_x)\).
拆开来,前半部分好算,转化为只要算长度为 \(k\) 的递增子序列的 \(cnt’_x\) 之和即可。
考虑 dp。
\(f_{i,k}\) 表示考虑以 \(i\) 结尾,选一共 \(k\) 个的递增子序列的 \(cnt'_x\) 之和。
\(g_{i,k}\) 表示考虑以 \(i\) 结尾,选一共 \(k\) 个的递增子序列个数。
\(g\) 是容易转移的。
对于 \(f\) 有:
\(\large f_{i,k} \leftarrow f_{j,k-1}[j<i][a_j<a_i]\)
还有在 \((j,i)\) 区间的贡献没有算。这个我们可以借助 \(g\) 来算。
相当于把贡献化成这个区间的每个数上。
这样我们就做完了
for (int i = 1;i <= n + 1;i ++ )
{
g[i][1] = 1;
f[i][1] = sg[1].query(a[i] );
for (int k = 2;k <= n;k ++ )
{
g[i][k] = sg[k - 1].query(a[i]);
f[i][k] =( sg[k].query(a[i] )+sf[k-1].query(a[i]))%mod;
}
rep(k,1,n)
{
sg[k].modify(a[i],g[i][k]);
sf[k].modify(a[i],f[i][k]);
}
}
int ans = 0 ;
vct<int> jc(n + 2,1);
rep(i,1,n) jc[i] = jc[i - 1] * 1ll * i % mod;
rep(k,1 ,n -1)
{
add(ans,(n - k) *1ll* jc[(n - k-1)]%mod*g[n+1][k+1]%mod);
auto t = -f[n + 1][k+1] *1ll * jc[n-k-1]%mod;
t = (t % mod + mod) % mod;
add(ans,t);
}
cout << ans << endl;
2
容斥角度考虑。
\(g_k\) 表示长度为 \(k\) 的子序列的数量。
删成这样的方案数:\(h_k = g_k \times (n-k)!\).
但是我们想删成这样之前又不想先变成递增的。
于是我们容斥一下得到。
方案数应该为 \(\sum_k h_k - h_{k + 1}\times(k + 1)\)。
P9225 「PEOI Rd1」寻宝(treasure)
题目描述
有一天 wrzSama 在寻宝,突然他掉到了一个神奇的房间里。这个房间里有 \(n\) 个机器,第 \(i\) 个机器可以生产 \(2^i\) 个钻石。
具体地,wrzSama 可以用 \(a_i\) 的时间开动第 \(i\) 个机器,让它生产 \(2^i\) 个钻石。这些机器有个很特殊的性质,每当他用一次第 \(i\) 个机器后,会让它的开动时间 \(a_i\) 加上 \(b_i\)。这意味着当他要第二次得到这 \(2^i\) 个钻石时就需要 \(a_i + b_i\) 的时间,每次不断累加,第 \(x\) 次开动就需要 \(a_i+(x-1)b_i\) 的时间。
wrzSama 需要得到至少 \(2^n\) 个钻石来得到宝藏,请问他最少需要多长时间。
做法
考虑朴素 dp。
列出来得到一个 \((min,+)\) 卷积的形式,发现这个代价肯定是凸的。
minko 做完了。
也可以从低位到高位用贪心理解。
void solvemain()
{
int n ;cin >> n;
vector<int> a(n + 2),b(n + 2);
for (int i = 1;i <= n;i ++)
cin >> a[i];
for (int i = 1;i <= n;i ++)
cin >> b[i];
const int B = sqrt(1e7) + 2;
vct<int> now;
for (int i = 1 ;i <= B;i ++ )
now.pb((i - 1) * b[1] + a[1]);
////////
for (int i = 2;i <= n;i ++ )
{
vct<int> cur,nnow;
for (int j = 1 ;j <= B;j ++ )
cur.pb((j - 1) * b[i] + a[i]);
for (int i = 0 ;i < sz(now) - 1;i += 2)
{
nnow.pb(now[i] + now[i + 1]);
}
now.assign(sz(cur) +sz(nnow) ,0);
merge(all(cur),all(nnow),now.begin());
}
cout << now[0] << endl;
}
树的直径性质记录
- 一个点的最远点,一定可以在某一对直径端点中找到。
- 树的中心最多两个
- 一棵树的所有直径必定相交(至少共享一个顶点)。不可能存在两条完全不相交的直径。
- 两树合并,直径必然产生与他们各自直径端点的路径。
- 树的所有直径中点重合.
CF2253E Diameter Intersections
题目描述
给定一棵有 \(n\) 个节点的树。树的直径是指树中长度最长的简单路径。路径的长度是其包含的边的数量。已知给定的树的直径长度是奇数。
称一个整数 \(k\) 是“美丽的”,如果存在两条树的直径(可以有相同的两个端点,也允许选择同一条直径),使得它们的交集中恰好包含 \(k\) 条边。
请找出所有美丽的 \(k\) 值,并按照递增顺序输出。
做法
注意到直径长度奇数,手玩后容易发现所有直径必然经过中间的边。
我们就拆成左右两个子树做,分别延伸最长。
\(f_x ,g_x\) 分别代表两个子树分别两条路径延伸最长交集为 \(x\) 是否可能。
这个是好求的。
然后我们合并一下,发现是卷积形式做不了。
再注意一下,有这样的事情:
所以 \(\# f_x = 1 < \sqrt n\),直接暴力合并。
CF2253F 4-beauty
题目描述
对于一个整数集合 \(S\),定义其可整除性特征为满足 \(x \ne y\),\(x\) 和 \(y\) 都属于 \(S\),且 \(x\) 能被 \(y\) 整除的有序对 \((x, y)\) 的数量。
给定一个整数集合 \(A\),定义其 \(4\)-美丽度如下:
- 考虑所有属于 \(A\) 且能组成等差数列的四个不同数的集合;
- 在所有这样的集合中,找到可整除性特征的最大值。
如果不存在满足条件的四元组,则 \(4\)-美丽度为 \(0\)。
最初给定集合 \(\{1, 2, \ldots, n\}\)。你可以从中移除若干数。移除数字 \(i\) 需要花费 \(m_i\) 个硬币。
请计算,为了减少该集合的 \(4\)-美丽度,最少需要花费多少个硬币。
做法
套路题。
先把美丽度式子写出来。
对于 \(a,a+d,a+2d,a+3d\),
容易得到 \([d\mod a=0] +[2d\mod a=0] + [3d \mod a =0] + [a=d]\)。
容易发现当且仅当 \(a=d\) 美丽度最大为 \(4\)。
且 \(1,2,3,4\) 就是一组解。
问题转化为删去若干个数使得集合内不存在 \(a,2a,3a,4a\)。
这个就是经典问题 P3226 集合选数了。
直接将数拆成 \(x\times 2^{k_2} \times 3^{k_3}\)。
然后将 \(x\) 相同的数画成一个矩阵,大概是这样:
1 2 4 8 16 32
3 6 12 24 48 96
9 18 36 ...
向右乘 \(2\),向下乘 \(3\)。
对于 \(a,2a,3a,4a\) 也在矩阵中表现为一个 \(L\) 型。
于是我们就可以状压 dp 了。
这里数据范围有点小大,我们考虑轮廓线 dp。
逐个选,然后把轮廓线记录在状态中即可。
计算次数大概是 \(\sum_x k_2\times k_3\times 2^{k_2}\)。
打个表算一下发现只有 \(2\times10^8\) 左右,可以通过。
int n;cin >> n;
vector<ll> cost (n + 2),pw3(17);
rep(i,0,16) pw3[i] = (i)?pw3[i-1] * 3 : 1;
rep(i,1,n) cin >> cost[i];
ll ans = 0 ;
auto chk = [&](ll &x,ll y)
{
if (x > y) x = y;
};
for (int x = 1;x <= n;x++)
{
if(x%2!=0 && x % 3 != 0)
{
//
int k2 = __lg(n / x);
int k3 = log(n / x) / log(3);
vct<ll>f(1<<(k2+1),1e18); //last
f[(1<<(k2+1)) - 1] = 0;
auto g = f;
for (int i = 0 ;i <= k3;i ++ )
for (int j = 0 ;j <= k2;j ++ )
{
int num = x * pw3[i] * (1<<j);
// dbg(i,j,num);
g.assign((1<<(k2+1)),1e18);
rep(st,0,(1<<(k2+1)) - 1)
{
if (f[st] > 1e16) continue;
//xuan
if (num <= n)
chk(g[st|(1<<j)],f[st]+cost[num]);
else chk(g[st|(1<<j)],f[st]);
// buxuan
if (num <= n)
{
if (j >= k2 - 1 ||
(st>>j&1) || (st>>(j+1)&1)
|| (st>>(j+2) & 1))
{
int nst = st;
if (nst >> j & 1)nst^=(1<<j);
chk(g[nst],f[st]);
}
}
}
f = g;
}
ans += *min_element(all(f));
}
}
cout << ans << endl;
P10992 [蓝桥杯 2023 国 Python A] 最长同类子串
题目描述
对于两个等长的字符串 \(A, B\),如果对于任意 \(i, j\),都有 \(A_i = A_j\) 和 \(B_i = B_j\)
同时满足或同时不满足,那么我们称 \(A, B\) 是一对同类串。例如,aabab 和 xxkxk 是一对同类串,而 abcde 和 abcdd 则不是。
给定 \(S, T\),找出一个尽可能大的\(k\) 使得 \(S, T\) 分别含有一个长度为 \(k\) 的子
串 \(S', T'\),且 \(S',T'\) 是同类串。
做法
先二分转判定。
对同类串 hash。
相当于变成 26 组集合凑成的多重集相同。
\(\{\{1,2,4\},\{3,5\}\}\)。
然后有滑动窗口需要支持删除插入,全局 \(+1\) 维护 hash。
容易发现瓶颈是全局 \(+1\),这个怎么做?
直接考虑多项式哈希,\(\sum_i B_i \times [i\in S_k]\) 为集合 \(k\) 的 \(hash\)。
然后我们把每个集合的 \(hash\) 异或起来即可做到多重集 hash。
P17315 [KismetOI 2026 I] Gujyo Senpai and Chin-Lan's Game
题目描述
郡上与景岚正在玩游戏。她们有一副 \(n\) 张牌,点数为 \(1\) 至 \(n\) 的牌各一张。定义一个牌序列是美好的,当且仅当该牌序列可以由一次下述过程生成:
将这 \(n\) 张牌按点数升序排序,将其划分成若干连续段,然后对于每个连续段,选择其中一张牌挪到此段末尾(例如,段 \((1,2,3,4)\) 操作后可以变为 \((2,3,4,1),(1,3,4,2),(1,2,4,3),(1,2,3,4)\)),最后将所有段按原顺序拼合起来,得到一个新的序列。
现在景岚设计了一个美好的牌序列并将其背面朝上摆成一排,郡上来猜点数为 \(1\) 的牌所在的位置。具体地,郡上会进行多次询问,每次给出一个下标并得知这个位置上牌的点数,直到结果为 \(1\) 并结束游戏。郡上非常聪明,所以她每次都会选择可以使最劣情况下自己总查询次数最小的下标进行询问,注意郡上知道这个序列是美好的。
然而景岚有高超的手法,她可以快速互换郡上尚未查询过的两副牌的位置。为了不被郡上发现,她需要保证游戏结束时,至少存在一个美好的序列使得郡上已知位置的点数与其一致。她希望最大化郡上结束游戏时的查询次数,并在此基础上最小化游戏结束时牌序列的字典序。如果游戏结束时仍有未知位置,将牌序列视作所有与已知位置完全匹配的合法牌序列中字典序最小的一个。
现在你来扮演景岚,交互库会进行多次查询,每次查询给定一个下标,你需要返回景岚给出的这个位置上牌的点数。
做法
1
肯定先考虑刻画美好序列。
容易发现等价于选若干个数向后移动且移动路径不交。
还是不好做。
再仔细刻画一下。
一次洗牌相当于,一个连续段左移加上开头向后移。
于是想到考虑增量。也就是令 \(c_i = a_i-i\)。
发现 \(c_i\) 形如:
1 1 1 1 -4 0 0 1 1 -2 0 0 1 -1 0 0 0 0 0 1 1 1 1 1 1 -6
一段连续的 \(1\) 跟着这段 \(1\) 的长度相反数再跟着若干零以此类推。
且 \(c_i\) 可以与 \(a_i\) 相互转化。
2
我们就可以在 \(c_i\) 上考虑问题了。答案是 \(c_i\) 第一个 \(\le 0\) 的位置。
观察样例,我们猜测最大查询次数是 \(n\)。
也就是说当有 \(n-2\) 个位置已知时,仍然存在构造使得合法序列不唯一。
考虑这样构造:
1 1 1 1 1 ? 1 1 1 1 1 1 ? ...
为什么呢?
第一个问号前显然必须 \(>0\),不然答案已经出现了。
中间若存在 \(\le 0\),容易发现也必然出现答案。
像这样构造,第一个问号有两种合法填法 \(1\) 或者 \(-x\)。
所以交互库必须查询完所有的位置才能获得答案。
3
这题还需要在线构造字典序最小方案。
思路也很简单。设当前查询位置 \(x\)。
若 \(x\) 后面还有未查询位置,由上面构造方案,未查询位置很可能成为最后的 \(?\)。
于是我们只能令 \(c_x = 1\)。
反之,我们希望最小化 \(c_x\),于是令 \(c_x\) 为前面连续 \(1\) 的个数的相反数。
代码
int vis[N],ed;
void init(int c,int n)
{
ed = n;
}
int query(int id)
{
int ret = 0;
if (id == ed)
{
vis[id] = 1;
while (vis[ed]) ed--;
ret = id - ed - 1;
}
else
{
vis[id] = 1;
ret = id + 1;
}
return ret;
}
P11749 「TPOI-1C」Standard Problem.
形式化的,给定字符串 \(s\) 和整数 \(k\),求 \(s^k\)(字符串 \(s\) 拼接 \(k\) 次)有多少回文子串(位置不同即算作不同),模 \(998244353\)。
做法
一个循环的串的回文肯定有点性质,我们研究一下。
假设有循环 \(SSSSSSS\)。
\(T[i] = T[i +|S|]\)。
设有回文 \(T[l,r](r-l+1\ge |S|)\)。
考察 \(T[l-1],T[r+1]\)。
有 \(T[l-1] = T[l-1+|S|]=T[r+1-|S|]=T[r+1]\)。
所以可以无限延伸。
我们把一个回文子串按照:
- 左端点在哪个块;
- 右端点在哪个块;
分类。
\(C_d\) 表示左端点在某一块,右端点在右边第 d 个块的回文子串数量。
\(ans = sum_d C_d \times(k + 1 - d)\)。
有结论 \(c_k = c_3,\forall k\ge 3\)。
设中心 \(mid\)。
\(T[i] = T[i +n],T[i] = T[2\times mid -i]\)。
将 \(mid\) 移动 \(\frac{n}{2}\)。
\(T[2mid' - i] = T[2mid+n-i] = T[i]\)。
故 \(mid + \frac{n}{2}\) 也是回文中心。
所以对于任意横跨 \(d\) 块的回文,我们都能把他变成 \(d+1\) 块的回文,通过增加一个周期。
构成双射。
故 \(C_d = C_{d+ 1}\).
再递推一下。
\(C_1 = A_1\)
\(C_2 =A_2 - 2C_1\)
\(C3 = A_3 - 3C_1 - 2C_2\)。
做完了。
还有一种思考方式就是考虑 \(C_k \rightarrow C_{k + 2}\)。
只需要左右各自增长 \(n\) 即可。

浙公网安备 33010602011771号