爆灵力……
CF995E Number Clicker
很有意思的题。
算法一:双向广搜
据说状态数最多 (10^7),可过。
算法二:随机化
首先介绍一个前置理论:生日攻击。
这个理论最通俗的描述是说,(23) 个人中有两个人生日相同的概率超过 (50\%)(假设一年有 (365) 天),(70) 个人时概率为 (99.9\%),而在人数达到 (200) 时,概率已经非常逼近 (100\%)(准确说是 (1-2 imes 10^{-29}))。
这个理论常用于哈希碰撞攻击。形式化地,对于一个值域范围为 (n) 的整值函数,设其定义域范围为 (k) 时出现相同值的概率为 (p),则有
其中 (n^{underline{k}}) 为 (n) 的 (k) 次下降幂。
解出这个方程可得:当 (k>sqrt{frac{pi}{2}n}) 时,(p>50\%)(具体做法康这里)。
所以我们只需为 (u,v) 分别随机生成 (sqrt{p}) 条长度为 (100) 的操作路径即可求解,复杂度 (O(sqrt{p}log p))。
算法三:这叫啥啊我也不知道
期望更优的方法,很神奇。
考虑为 (u,v) 分别生成一个到 (0) 的操作路径,具体方法是随机取一个整数 (xin [1,p-1]),记 (aequiv uxpmod p,b=x),对 ((a,b)) 使用更损相减术迭代至 (b=0)。考虑这么做的意义,由于 (uequivdfrac{a}{b}pmod p),那么减一操作相当于 (dfrac{a}{b} o dfrac{a-b}{b}pmod p),求逆操作相当于 (dfrac{a}{b} odfrac{b}{a}pmod p) 太神奇了!
对 (v) 同理,由于操作可逆,所以只需将 (v) 的操作中所有的减换成加即可。
当然在实际实现中我们得先用辗转相除计算路径长度,如果小于 (100) 再用更损相减。
官方题解说这个做法“It happens to take a few steps in most cases, but we have no proof.”
(代码跑了 0ms,我惊了
#include <bits/stdc++.h>
using namespace std;
mt19937 rng(1919810);
const int N=105;
int u,v,p,s1[N],s2[N];
int gcd(int a,int b) {return b?gcd(b,a%b)+a/b+1:0;}
int solve(int n,int s[])
{
int a,b,cnt=0;
do b=rng()%(p-1)+1,a=1LL*b*n%p;
while(gcd(a,b)>100);
while(b)
if(a<b) s[++cnt]=3,swap(a,b);
else s[++cnt]=2,a-=b;
return cnt;
}
int main()
{
scanf("%d%d%d",&u,&v,&p);
int len1=solve(u,s1),len2=solve(v,s2);
printf("%d
",len1+len2);
for(int i=1;i<=len1;++i) printf("%d ",s1[i]);
for(int i=len2;i;--i) printf("%d ",s2[i]==2?1:3);
return 0;
}
CF300D Painting Square
这道题 CF 上 (k<1000),所以可以不用 NTT 水过,但毒瘤把数据加强到 (k<50000) 了,只能用 NTT 了 555……
还没学 NTT,先把式子放到这吧(早晚会学的,口亨~)。
先考虑一个显然的 DP,记 (f_{i,j}) 表示边长为 (2^i+1) 的正方形切 (j) 刀的方案数,有
设 (g_{i,j}=sumlimits_{a+b=j}f_{i,a}f_{i,b}),则 (f_{i,j}=sumlimits_{a+b=j-1}g_{i-1,a}g_{i-1,b})。
直接算 (O(k^2log n)),NTT 优化后 (O(klog klog n))。
CF724E Goods Transportation
部分分有一个显然的网络流,由于 (n<10000) 过不了。
联想到最大流=最小割,又由于本题的图建得非常特殊,所以联想到要用 DP 模拟最小割。记 (f_{i,j}) 表示前 (i) 个点其中有 (j) 个点属于 (S) 集时的最小割。分别讨论 (i) 属于 (S) 集与 (T) 集的情况,如果属于 (S) 集,则最小割加 (s_i),否则最小割加 (p_i+j imes c)。注意需要滚动数组。
(O(n^2)) 过 (10000) 可还行(发抖
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,c,p[N],s[N];
long long f[2][N];
int main()
{
scanf("%d%d",&n,&c);
for(int i=1;i<=n;++i) scanf("%d",p+i);
for(int i=1;i<=n;++i) scanf("%d",s+i);
for(int i=1;i<=n;++i)
{
memset(f[i&1],0x3f,sizeof(f[i&1]));
for(int j=0;j<=i;++j)
{
f[i&1][j]=f[(i^1)&1][j]+p[i]+1LL*j*c;
if(j) f[i&1][j]=min(f[i&1][j],f[(i^1)&1][j-1]+s[i]);
}
}
printf("%lld
",*min_element(f[n&1],f[n&1]+n+1));
return 0;
}