[BZOJ3771] Triple

Description

有个沙雕樵夫有n把价值互不相同的斧头,某天一个沙雕水神偷走了这个樵夫的一把或两把或三把斧头。樵夫的总损失值就是被偷走的斧头价值和。他想知道对于每个可能的总损失,有几种可能的方案。

注意:如果水神拿走了两把斧头a和b,(a,b)和(b,a)算一种方案。拿走三把斧头时,(a,b,c),(b,c,a),(c,a,b),(c,b,a),(b,a,c),(a,c,b)也只算一种方案。

a_i<=40000。

Solution

这沙雕题面还真是悲伤啊哈哈哈哈哈哈

可以搞出一个生成函数(总觉得就是个桶),就是拿走一个斧头的方案数f(x)

两个数的方案数就是(f*f)(x),但是其中包含了两次使用同一个数的方案数h(x)=f(2x),而其余的方案数统计了两次,所以方案数为(f*f-h)(x)/2

三个数的方案数就是(f*f*f)(x),然后减去三次使用同一个数的方案数g(x)=f(3x),再减去两次使用同一个数的方案数(f*h-g)(x)*3 (里面减掉g是因为三次使用同一个数的方案数被重复计算,再乘3是因为在(f*f*f)(x)中计算了三次((x,x,y),(x,y,x),(y,x,x)),而其余的方案统计了6次,所以方案数为(f*f*f-3*h*f+2*g)(x)/6

总感觉在点值表达那里直接乘起来解释不通所以我是在系数表达那块才乘起来的

还有FFT的空间开几倍是个玄学问题

Code

还是改掉了namespace码风因为实在是太鬼畜了

#include<bits/stdc++.h>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
const int N=5e5+5;
const db Pi=acos(-1);

int n,lim,rev[N],val[N],s[N];

struct cp{
    db x,y;
    cp (db a=0,db b=0){x=a,y=b;}
    friend cp operator+(cp a,cp b){return cp(a.x+b.x,a.y+b.y);}
    friend cp operator-(cp a,cp b){return cp(a.x-b.x,a.y-b.y);}
    friend cp operator*(cp a,cp b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
}f[N],f2[N],f3[N],h[N],g[N],hf[N];

int getint(){
    int X=0,w=0;char ch=getchar();
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while( isdigit(ch))X=X*10+ch-48,ch=getchar();
    if(w) return -X;return X;
}

void fft(cp *f,int opt){
    for(int i=0;i<lim;i++) if(i<rev[i]) swap(f[i],f[rev[i]]);
    for(int mid=1;mid<lim;mid<<=1){
        cp tmp=cp(cos(Pi/mid),sin(Pi/mid)*opt);
        for(int R=mid<<1,l=0;l<lim;l+=R){
            cp w=cp(1,0);
            for(int k=0;k<mid;k++,w=w*tmp){
                cp x=f[l+k],y=f[l+k+mid]*w;
                f[l+k]=x+y,f[l+k+mid]=x-y;
            }
        }
    }
}

signed main(){
    n=getint();int mx=0;
    for(int i=1;i<=n;i++) val[i]=getint(),s[val[i]]++,mx=max(mx,val[i]),f[val[i]].x++,h[val[i]*2].x++,g[val[i]*3].x++;
    lim=1;while(lim<=4*mx) lim<<=1;
    for(int i=1;i<lim;i++) rev[i]=(rev[i>>1]>>1)|(i&1?lim>>1:0);
    fft(f,1),fft(h,1);
    for(int i=0;i<lim;i++) hf[i]=h[i]*f[i],f2[i]=f[i]*f[i],f3[i]=f[i]*f[i]*f[i];
    fft(hf,-1),fft(f,-1),fft(f2,-1),fft(f3,-1),fft(h,-1);
    for(int i=0;i<lim;i++){
        int x=(int)(f[i].x/lim+0.5)+((int)(f2[i].x/lim+0.5)-(int)(h[i].x/lim+0.5))/2+((int)(f3[i].x/lim+0.5)-(int)(hf[i].x/lim+0.5)*3+g[i].x*2)/6;
        if(x) printf("%d %d\n",i,x);
    } return 0;
}

当你走进这欢乐场
posted @ 2018-12-12 09:41  YoungNeal  阅读(177)  评论(0编辑  收藏  举报