专栏文章

题解:UVA1718 Tile Cutting

UVA1718题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mint4lsx
此快照首次捕获于
2025/12/02 07:55
3 个月前
此快照最后确认于
2025/12/02 07:55
3 个月前
查看原文
咋没有人写倍增的 FFT?
V=max{r}V=\max\{r\}
先预处理出 f(m)f(m)。对于一个平行四边形,可以用一个矩形框起来,四个点的坐标分别是 (a,0),(0,c),(a+b,d),(b,c+d)(a,0),(0,c),(a+b,d),(b,c+d)
这个平行四边形的面积就是 (a+b)(c+d)acbd=ad+bc(a+b)(c+d)-ac-bd=ad+bc。因此 f(m)=a,b,c,d[ad+bc=m]f(m)=\sum_{a,b,c,d}[ad+bc=m]
因此答案就是对 ad+bc=mad+bc=m 计数。
先对 g(m)=a,b[ab=m]g(m)=\sum_{a,b}[ab=m] 计数,发现是 mm 的因数个数,可以通过埃氏筛在 O(VlogV)O(V\log V) 的时间复杂度内求出。
发现 f(m)=g(i)g(mi)f(m)=\sum g(i)g(m-i),是一个卷积的形式,使用 FFT,求多项式 gg 的平方就能得到 ff
然后就是静态 RMQ 问题,这个可以使用 ST 表解决。
时间复杂度 O(VlogV+n)O(V\log V+n)
CPP
#include<bits/stdc++.h>
#define ll long long
#define db long double
using namespace std;
constexpr db eps=1e-8,pi=3.14159265358979323846;
constexpr ll R=5e5+5,maxn=2e6+10;
struct compx{
    db x,y;
}f[maxn];
compx operator +(compx a,compx b){
    return (compx){a.x+b.x,a.y+b.y};
}
compx operator -(compx a,compx b){
    return (compx){a.x-b.x,a.y-b.y};
}
compx operator *(compx a,compx b){
    return (compx){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}
ll r[maxn],c[maxn],n,dp[maxn][22],ans[maxn];
void FFT(compx *a,ll lim,ll opt){
    for(int i=0;i<lim;i++){
        if(i<r[i])swap(a[i],a[r[i]]);
    }
    for(int len=1;len<lim;len<<=1){
        compx mul={1,0},w={cos(pi/len),opt*sin(pi/len)};
        for(int i=0;i<lim;i+=(len<<1)){
            mul={1,0};
            for(int j=0;j<len;j++){
                compx a0=a[i+j],a1=mul*a[i+j+len];
                a[i+j]=a0+a1;
                a[i+j+len]=a0-a1;
                mul=mul*w;
            }
        }
    }
    if(opt==-1){
        for(int i=0;i<lim;i++){
            a[i].x=(a[i].x/lim);
        }
    }
}
void mul(compx *a,ll len){
    ll lim=1,L=0;
    while(lim<=len+len){
        lim<<=1;
        L++;
    }
    for(int i=0;i<lim;i++){
        r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
    }
    FFT(a,lim,1);
    for(int i=0;i<lim;i++)a[i]=a[i]*a[i];
    FFT(a,lim,-1);
}
ll maxnum(ll a,ll b){
    return (ans[a]>=ans[b])?a:b;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    for(int i=1;i<=R;i++){
        for(int j=i;j<=R;j+=i){
            c[j]++;
        }
    }
    for(int i=1;i<=R;i++){
        f[i]={1.0*c[i],0};
    }
    mul(f,R);
    for(int i=1;i<=R;i++){
        ans[i]=(ll)(f[i].x+0.5);
        dp[i][0]=i;
    }
    for(int j=1;j<22;j++){
        for(int i=1;i+(1<<(j-1))<=R;i++){
            //dp[i][j] 表示从 i 开始到 i+2^{j}-1 结束的最大下标
            dp[i][j]=maxnum(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
    cin>>n;
    while(n--){
        ll x,y;cin>>x>>y;
        ll k=__lg(y-x+1);
        ll cur=maxnum(dp[x][k],dp[y-(1<<k)+1][k]);
        cout<<cur<<" "<<ans[cur]<<"\n";
    }
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...