
日记
想说点什么但是说不出来。
Transforming Pairs SLink to
也是切上绿题了。
看到这玩意很像 的求法,我们看看能不能往 上面想。
口胡了一个做法:我们钦定 。首先我们对 进行操作。将 减去 。这样可以保证 。对 进行同样的操作,减去 ,同样为了保证 。
继续口胡的证明:感性理解,我们为了让 和 尽快的减成 和 ,每次减的肯定要尽可能多。于是我们每次进行辗转相减,减到再减一次就会爆炸的程度,每次减得就尽可能多了。
Moo Decomposition GLink to
Java 的常数还是太优秀了。
我们发现 ,直接把整个序列拎出来做肯定是不现实的。但是我们发现一个性质:每段的方案数相互独立。那么我们就可以把每段的方案数单独求出来然后快速幂了。
考虑如何求每段的方案数:从后往前枚举每个字符,我们设 表示当前遇到的 的个数。
- 遇到了个
显然是 。 - 遇到了个
这个 可以从前面的 个 里面任意取 个,方案数乘上 。然后因为用掉了 个 ,我们需要把 减去 。
Bessie’s Function GLink to
一道基环树板子题硬控我一下午。
我也并非人类。
我们发现这玩意可以套一个典中典的模型:对于每个 ,连一条 的边。因为总共有 条边,所以一定是一个基环树森林。
我们发现如果要修改 ,一定是要把 修改成 ,因为这样不仅可以使得 变得满足条件,还能使出边连接到 的点满足条件。但是为了方便 dfs,不可能建内向的基环树,肯定是建外向的基环树,就变成了让 可以使得 的儿子们也满足条件,我们就可以把题意转化一下:
对于每个点 ,都可以花费代价 进行一次操作。操作必须要满足以下条件:如果你不操作那你的所有前驱就必须操作(在树上“前驱”表示儿子,在环上表示上一个节点。),反之你的前驱爱咋咋地。求最小代价。
我们这个题意都把方程给你写出来了。设 表示 操作或者不操作。在树上就是:
在环上也类似。如何处理环上 dp 呢?我们考虑钦定 的状态。比如我们钦定 选,那么 就设成 ,最后统计答案最后一个点 就爱选不选,。反之就是 ,。最后 加上两个 的最小值就是了。
以后有向图找环还是老老实实 dfs 吧。
Shorten the ArrayLink to
都想到了 了为啥不再想一会呢?
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
#include<bits/extc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 2e5 + 5;
int n,k,cnt;
int ch[maxn * 30][2],pos[maxn * 30];
void ins(int x,int id)// 字典树插入
{
int rt = 1;
for (int i = 30; i >= 0; i--)
{
bool dig = (x >> i) & 1;
if (!ch[rt][dig])
ch[rt][dig] = ++cnt;
rt = ch[rt][dig];
pos[rt] = max(pos[rt],id);// 维护这个这个子树内的最大 id
}
}
int que(int x)
{
int sum = 0,res = 0,rt = 1;
for (int i = 30; i >= 0; i--)
{
bool dig = (x >> i) & 1;
if (ch[rt][dig ^ 1])
{
if ((sum ^ (1 << i)) >= k)
{
res = max(res,pos[ch[rt][dig ^ 1]]);
rt = ch[rt][dig];
// 如果可以 >= k 就记录答案,然后往另一个子树走,看看能不能更大。
}
else
{
// 否则肯定要异或。
sum ^= (1 << i);
rt = ch[rt][dig ^ 1];
}
}
else if (ch[rt][dig])
rt = ch[rt][dig];
else
break;
}
return res;
}
void solve()
{
for (int i = 1; i <= cnt; i++)
ch[i][0] = ch[i][1] = pos[i] = 0;
cnt = 1;
scanf("%d%d",&n,&k);
int ans = inf;
for (int i = 1,x; i <= n; i++)
{
scanf("%d",&x);
ins(x,i);
if (int tmp = que(x); tmp)
ans = min(ans,i - tmp + 1);
}
if (k == 0)
return (void)puts("1");
if (ans == inf)
puts("-1");
else
printf("%d\n",ans);
}
signed main()
{
int t;
scanf("%d",&t);
while (t--)
solve();
return 0;
}
后日谈 Link to 后日谈
今天也是终于把 Java 的传参搞懂了。
日记
© 伊埃斯 | CC BY-NC-SA 4.0