bzoj 4836: [Lydsy2017年4月月赛]二元运算 -- 分治+FFT

4836: [Lydsy2017年4月月赛]二元运算

Time Limit: 8 Sec  Memory Limit: 128 MB

Description

定义二元运算 opt 满足

现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问。每次询问给定一个数字 c

你需要求出有多少对 (i, j) 使得 a_i  opt b_j=c 。

Input

第一行是一个整数 T (1≤T≤10) ,表示测试数据的组数。

对于每组测试数据:

第一行是三个整数 n,m,q (1≤n,m,q≤50000) 。

第二行是 n 个整数,表示 a_1,a_2,?,a_n (0≤a_1,a_2,?,a_n≤50000) 。

第三行是 m 个整数,表示 b_1,b_2,?,b_m (0≤b_1,b_2,?,b_m≤50000) 。

第四行是 q 个整数,第 i 个整数 c_i (0≤c_i≤100000) 表示第 i 次查询的数。

Output

对于每次查询,输出一行,包含一个整数,表示满足条件的 (i, j) 对的个数。

Sample Input

2
2 1 5
1 3
2
1 2 3 4 5
2 2 5
1 3
2 4
1 2 3 4 5

Sample Output

1
0
1
0
0
1
0
1
0
1

HINT

Source

可能我的常数真的巨大。。。

转载一个姜大爷的题解http://blog.csdn.net/neither_nor/article/details/70889246

#include<map>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1000000007
#define ll long long
#define PI acos(-1)
#define M 400010
inline int rd()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
#define db double
struct cl{
    db x,y;
    cl(){}
    cl(db _x,db _y){x=_x;y=_y;}
    friend cl operator +(const cl &x,const cl &y){return cl(x.x+y.x,x.y+y.y);}
    friend cl operator -(const cl &x,const cl &y){return cl(x.x-y.x,x.y-y.y);}
    friend cl operator *(const cl &x,const cl &y){return cl(x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x);}
    friend cl operator /(const cl &x,const db &y){return cl(x.x/y,x.y/y);}
    cl con(){return cl(x,-y);}
};
cl a[M],b[M];
int n,L,R[M];
int A[M],B[M],mx,T;
ll ans[M];
void fft(cl *x,int f,int n)
{
    for(int i=0;i<n;i++) if(i<R[i]) swap(x[i],x[R[i]]);
    for(int i=1;i<n;i<<=1)
    {
        cl wn(cos(PI/i),f*sin(PI/i));
        for(int j=0;j<n;j+=(i<<1))
        {
            cl w(1,0),X,Y;
            for(int k=0;k<i;k++,w=w*wn)
            {
                X=x[j+k];Y=w*x[j+k+i];
                x[j+k]=X+Y;x[j+k+i]=X-Y;
            }
        }
    }
    if(f==-1) for(int i=0;i<n;++i) x[i]=x[i]/n;
}
void cal(int l,int r,int n,int L)
{
    if(l==r)
    {
        ans[0]+=(ll)A[l]*B[l];
        return ;
    }
    register int i,j,mid=l+r>>1;
    for(i=0;i<n;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L);
    for(i=0;i<n;++i) a[i]=b[i]=cl(0,0);
    for(i=l;i<=mid;++i) a[i-l].x=A[i];
    for(i=mid+1;i<=r;++i) a[i-mid-1].y=B[i];
    fft(a,1,n);
    for(i=0;i<n;i++)
    {
        j=(n-i)&(n-1);
        b[i]=(a[i]*a[i]-(a[j]*a[j]).con())*cl(0,-0.25);
    }
    fft(b,-1,n);
    for(i=0;i<n;++i) ans[l+mid+1+i]+=(ll)(b[i].x+0.1);

    for(i=0;i<n;++i) a[i]=b[i]=cl(0,0);
    for(i=mid+1;i<=r;i++) a[i-mid-1].x=A[i];
    for(i=l;i<=mid;i++) a[mid-i].y=B[i];
    fft(a,1,n);
    for(i=0;i<n;i++)
    {
        j=(n-i)&(n-1);
        b[i]=(a[i]*a[i]-(a[j]*a[j]).con())*cl(0,-0.25);
    }
    fft(b,-1,n);
    for(i=0;i<n;++i) ans[1+i]+=(ll)(b[i].x+0.1);

    cal(l,mid,n>>1,L-1);
    cal(mid+1,r,n>>1,L-1);
}
int main()
{
    register int n1,n2,q,i,x;
    T=rd();
    while(T--)
    {
        n1=rd();n2=rd();q=rd();mx=0;
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(ans,0,sizeof(ans));
        for(i=0;i^n1;++i)
        {
            x=rd();A[x]++;
            mx=max(mx,x);
        }
        for(i=0;i^n2;i++)
        {
            x=rd();B[x]++;
            mx=max(mx,x);
        }
        for(n=1,L=-1;n<(mx<<1);n<<=1) L++;
        cal(0,n-1,n,L);
        while(q--)
        {
            x=rd();
            printf("%lld\n",ans[x]);
        }
    }
    return 0;
}
时间: 2024-09-28 18:43:03

bzoj 4836: [Lydsy2017年4月月赛]二元运算 -- 分治+FFT的相关文章

【bzoj4836】[Lydsy2017年4月月赛]二元运算 分治+FFT

题目描述 定义二元运算 opt 满足 现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问.每次询问给定一个数字 c 你需要求出有多少对 (i, j) 使得 a_i  opt b_j=c . 输入 第一行是一个整数 T (1≤T≤10) ,表示测试数据的组数. 对于每组测试数据: 第一行是三个整数 n,m,q (1≤n,m,q≤50000) . 第二行是 n 个整数,表示 a_1,a_2,?,a_n (0≤a_1,a_2,?,a_n≤50000) . 第三行是 m

BZOJ 4883 [Lydsy2017年5月月赛]棋盘上的守卫(最小生成环套树森林)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4883 [题目大意] 在一个n*m的棋盘上要放置若干个守卫. 对于n行来说,每行必须恰好放置一个横向守卫:同理对于m列来说, 每列必须恰好放置一个纵向守卫.每个位置放置守卫的代价是不一样的, 且每个位置最多只能放置一个守卫,一个守卫不能同时兼顾行列的防御. 请计算控制整个棋盘的最小代价. [题解] 我们将每行和每列看成一个点,用每个格子上的点的权值作为边, 这就构成了一个n+m个点的图

BZOJ 4884 [Lydsy2017年5月月赛]太空猫(单调DP)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4884 [题目大意] 太空猫(SpaceCat)是一款画面精致.玩法有趣的休闲游戏, 你需要控制一只坐在迷你飞碟上的猫咪在太空里不断探索,让大家看看你能飞得多远. 游戏地图可以看成一个二维的网格图,上下是两段障碍物. 在游戏的一开始,太空猫位于地图最左边的下边界之上,且重力方向向下. 在每个时刻,你可以用手指点击屏幕,翻转重力的方向, 或者通过遥感控制太空猫往左或往右移动.每次翻转重力

【BZOJ 4832 】 4832: [Lydsy2017年4月月赛]抵制克苏恩 (期望DP)

4832: [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 275  Solved: 87 Description 小Q同学现在沉迷炉石传说不能自拔.他发现一张名为克苏恩的牌很不公平.如果你不玩炉石传说,不必担心,小Q 同学会告诉你所有相关的细节.炉石传说是这样的一个游戏,每个玩家拥有一个 30 点血量的英雄,并且可以用牌 召唤至多 7 个随从帮助玩家攻击对手,其中每个随从也拥有自己的血量和攻击力.小Q同学

[Lydsy2017年4月月赛]抵制克苏恩

[Lydsy2017年4月月赛]抵制克苏恩 时间限制: 1 Sec  内存限制: 128 MB提交: 49  解决: 34[提交][状态][讨论版] 题目描述 小Q同学现在沉迷炉石传说不能自拔.他发现一张名为克苏恩的牌很不公平.如果你不玩炉石传说,不必担心,小Q同学会告诉你所有相关的细节.炉石传说是这样的一个游戏,每个玩家拥有一个 30 点血量的英雄,并且可以用牌召唤至多 7 个随从帮助玩家攻击对手,其中每个随从也拥有自己的血量和攻击力.小Q同学有很多次游戏失败都是因为对手使用了克苏恩这张牌,所

【BZOJ4883】[Lydsy2017年5月月赛]棋盘上的守卫 KM算法

[BZOJ4883][Lydsy2017年5月月赛]棋盘上的守卫 Description 在一个n*m的棋盘上要放置若干个守卫.对于n行来说,每行必须恰好放置一个横向守卫:同理对于m列来说,每列 必须恰好放置一个纵向守卫.每个位置放置守卫的代价是不一样的,且每个位置最多只能放置一个守卫,一个守卫 不能同时兼顾行列的防御.请计算控制整个棋盘的最小代价. Input 第一行包含两个正整数n,m(2<=n,m<=100000,n*m<=100000),分别表示棋盘的行数与列数. 接下来n行,每

4832: [Lydsy2017年4月月赛]抵制克苏恩]【解题报告】

戳我 4832: [Lydsy2017年4月月赛]抵制克苏恩 时间限制: 1 Sec  内存限制: 128 MB提交: 752  解决: 289[提交][][] 题目描述 小Q同学现在沉迷炉石传说不能自拔.他发现一张名为克苏恩的牌很不公平.如果你不玩炉石传说,不必担心,小Q 同学会告诉你所有相关的细节.炉石传说是这样的一个游戏,每个玩家拥有一个 30 点血量的英雄,并且可以用牌 召唤至多 7 个随从帮助玩家攻击对手,其中每个随从也拥有自己的血量和攻击力.小Q同学有很多次游戏失败都是 因为对手使用

【BZOJ 4832】 [Lydsy2017年4月月赛] 抵制克苏恩 期望概率dp

打记录的题打多了,忘了用开维记录信息了......我们用f[i][j][l][k]表示已经完成了i次攻击,随从3血剩j个,2血剩l个,1血剩k个,这样我们求出每个状态的概率,从而求出他们对答案的贡献并加和,一开始我用的期望忘了转移的时候不能用1而要用概率...... #include <cstdio> #include <cstring> #define r register using namespace std; typedef long double LD; inline i

[bzoj4883][Lydsy2017年5月月赛]棋盘上的守卫

来自FallDream的博客,未经允许,请勿转载, 谢谢. 在一个n*m的棋盘上要放置若干个守卫.对于n行来说,每行必须恰好放置一个横向守卫:同理对于m列来说,每列 必须恰好放置一个纵向守卫.每个位置放置守卫的代价是不一样的,且每个位置最多只能放置一个守卫,一个守卫 不能同时兼顾行列的防御.请计算控制整个棋盘的最小代价. n*m<=10^5 费用流比较好想,把行和列拿出来,第i行向第j列连费用是a[i][j]的边,然后限制每行每列流量1即可. 但是费用流不是很科学(好像有人大力艹过了?),考虑优