AIZU 2560 [想法题]

表示敲完多项式乘法&高精度乘法两道FFT模板题后就开始来磕这题了

这题相对而言应该不算模板题 不过神犇们肯定还是一眼看穿

如果原OJ访问速度较慢的话可以考虑戳这里 http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=153505

------------------------------------------------------------------------------------------------------------------------------------------------

构造什么的 还是太巧妙了

比如对于这样的样例

2

1 2

3 4

可以构造出这样的矩阵(原矩阵在左上角 额外的矩阵在右下角 且两矩阵不相交且中心对称)

a矩阵               b矩阵

1 2 0 0           0 0 0 0

3 4 0 0           0 0 0 0

0 0 0 0           0 0 4 3

0 0 0 0           0 0 2 1

如果变为多项式的话 就是这样

多项式a  1 2 0 0 3 4 0 0 0 0 0 0 0 0 0 0

多项式b  0 0 0 0 0 0 0 0 0 0 4 3 0 0 2 1

当我们把它们乘起来后 会发现贡献到同一项点对的距离是一样的

当n不为2的整数幂的时候 可以像fft模板题一样 通过补零来进行构造

构造出这两个多项式后 我们就可以直接套fft模板 然后队结果多项式的有效的位数都访问一次即可统计出答案

------------------------------------------------------------------------------------------------------------------------------------------------

不过A掉后还是有一个问题还没弄懂

设多项式a,b的长度为n 那么按照常规的多项式乘法 我们应该把他们后面都补上n个0

因为结果多项式的长度为n×2 (虽然实际有效位数比n还小)

然而我参考的某AC代码 并没有用n×2的长度 而是只用到n的长度让结果 “自然溢出”了 (不是标准说法)

而且貌似n位之后的部分溢出到前面几位了

如果有神犇知道是为什么的话希望能回复下

#include <bits/stdc++.h>
using namespace std;
const int N=1<<23,M=1<<10;
const double pi=acos(-1);
typedef complex <double> E;
int mp[M][M];
int n,m,L;
int R[N];
long long cnt[N];
E a[N],b[N];
int sum;
double ans;
void fft(E *A,int f)
{
    for(int i=0;i<n;++i)
        if(i<R[i])
            swap(A[i],A[R[i]]);
    for(int i=1;i<n;i<<=1)
    {
        E wn(cos(pi/i),sin(pi/i)*f);
        for(int p=i<<1,j=0;j<n;j+=p)
        {
            E w(1,0);
            for(int k=0;k<i;++k,w*=wn)
            {
                E x=A[j+k],y=w*A[j+k+i];
                A[j+k]=x+y;
                A[j+k+i]=x-y;
            }
        }
    }
}
int getdist(int x,int y)
{
    return x*x+y*y;
}
int main()
{
    scanf("%d",&m);
    for(n=1;n<m*2;n<<=1)
        ++L;
    for(int i=0;i<m;++i)
        for(int j=0;j<m;++j)
        {
            scanf("%d",&mp[i][j]);
            sum+=mp[i][j];
            cnt[0]-=mp[i][j];
            a[i*n+j]=mp[i][j];
            b[(n-i-1)*n+(n-j-1)]=mp[i][j];
        }
    n=n*n*2;
    L=L*2+1;
    for(int i=0;i<n;++i)
        R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    fft(a,1);
    fft(b,1);
    for(int i=0;i<n;++i)
        a[i]*=b[i];
    fft(a,-1);
    int tn=sqrt(n/2)/2,x,y;
    x=tn;
    y=tn;
    for(int i=4*tn*tn-2*tn-1;i;--i)
    {
        int tmp1;
        if(y>=tn)
            tmp1=getdist(x-(tn*2-1),y-(tn*2-1));
        else
            tmp1=getdist(x-1-(tn*2-1),tn*2-1-(tn*2-1-y-1));
        double tmp2=a[x*tn*2+y].real()/n;
        ans+=sqrt(tmp1)*tmp2;
        cnt[tmp1]+=tmp2+0.5;
        x+=(y==tn*2-1);
        y=(y==tn*2-1?0:y+1);
    }
    printf("%.10f\n",ans/((long long)sum*(sum-1)));
    int flag=0;
    for(int i=0;i<m*m*2;++i)
        if(cnt[i])
        {
            printf("%d %lld\n",i,cnt[i]/2);
            if(++flag>=10000)
                break;
        }
    return 0;
}

还是把参考的那个只用了n的长度的多项式的神犇的代码贴一下(如果原作者有意见的话我会删掉的……)

#include<stdio.h>
#include<algorithm>
#include<complex>
#include<vector>
#include<math.h>
#include<map>
using namespace std;
const double PI = 4.0*atan(1.0);
typedef complex<double> Complex;
const Complex I(0, 1);
void fft(int n, double theta, Complex a[]) {
  for (int m = n; m >= 2; m >>= 1) {
    int mh = m >> 1;
    for (int i = 0; i < mh; i++) {
      Complex w = exp(i*theta*I);
      for (int j = i; j < n; j += m) {
        int k = j + mh;
        Complex x = a[j] - a[k];
        a[j] += a[k];
        a[k] = w * x;
      }
    }
    theta *= 2;
  }
  int i = 0;
  for (int j = 1; j < n - 1; j++) {
    for (int k = n >> 1; k > (i ^= k); k >>= 1);
    if (j < i) swap(a[i], a[j]);
  }
}
int b[1025][1025];
Complex p[1<<22];
Complex q[1<<22];
int main(){
    int a;
    scanf("%d",&a);
    int cnt=0;
    for(int i=0;i<a;i++){
        for(int j=0;j<a;j++)scanf("%d",&b[i][j]);
    }
    int n=1;
    while(n<a*2)n*=2;
    for(int i=0;i<a;i++)for(int j=0;j<a;j++){
        cnt+=b[i][j];
        p[i*n+j]=q[(n-1-i)*n+(n-1-j)]=Complex(b[i][j],0);
    }
    fft(n*n,2*PI/n/n,p);
    fft(n*n,2*PI/n/n,q);

    for(int i=0;i<n*n;i++)p[i]=p[i]*q[i];
    fft(n*n,-2*PI/n/n,p);
    for(int i=0;i<n*n;i++)p[i]/=n*n;
    /*for(int i=0;i<n;i++){
        for(int j=0;j<n;j++)printf("%.0f ",p[i*n+j].real());
        printf("\n");
    }*/
    double ret=0;
    long long sz=0;
    map<int,long long>m;
    for(int i=0;i<n*n;i++){
            int pi=i/n;
            int pj=i%n;
            if(pj>=n/2)continue;
            if(pj==0&&pi>=n/2)continue;
            long long v=(long long)(p[n*n-1-i].real()+0.5);
            if(i==0){v-=cnt;v/=2;}
            if(!v)continue;

            int I=min(pi,n-pi);
            int J=pj;
            int t=I*I+J*J;
            if(m.count(t)){
                m[t]+=v;
            }else{
                m[t]=v;
            }
            sz+=v;
            ret+=sqrt(t)*v;
    }
    printf("%.12f\n",ret/sz);
    int at=0;
    for(map<int,long long>::iterator it=m.begin();it!=m.end();it++){
        if(at==10000)break;
        at++;
        printf("%d %lld\n",(*it).first,(*it).second);
    }
}
时间: 2024-10-14 01:23:30

AIZU 2560 [想法题]的相关文章

POJ 2965 The Pilots Brothers&#39; refrigerator (想法题)

POJ 2965 题意: 输入一个形如: -+-- ---- ---- -+-- 4*4图案,+表示close,-表示open,定义一种操作为:改变某个单元格符号(+变-,-变+),同时单元格所在行与所在列的所有单元格符号都会发生改变. 求最少操作次数能使所有单元格内都是'-'.并输出要操作的单元格. 思路: 正常的做法和POJ 1573类似,dfs枚举即可,见code1. 这里提供一种高效的做法: 通过思考我们可以验证,某一个单元格内符号为'+',同时对其所在行与所在列的所有单元格进行操作(其

HDU 2410 Barbara Bennett&#39;s Wild Numbers (想法题)

题目链接:HDU 2410 Barbara Bennett's Wild Numbers 题意:给出两串数w,s(长度相同),第一串中有"?",问"?"取的值使w对应的数大于s对应的数 的最大方案数. 思路:W,S一一对应比较: 遇到第一个'?'之前比较情况 1.w[i]<s[i] 方案数0种:break: 2.w[i]>s[i] break.之后有n个''?' 方案数就有10的n次方种. 3.w[i]=s[i] 继续比较,重复1.2两个条件. 遇到'?

SGU 410 Galaxy in danger --贪心,想法题

题意:有n个星球,每个星球有Ai个人,每次有两种选择,第一是从每个星球上去掉1个人,第二个选择是选择一个星球放置一个科学家,将该星球的人数加倍,问最少多少次能够将所有星球上的人数同时变为0,并且如果步数<=1000,还要输出操作顺序. 解法:找出人数最多的那个星球,设最大人数为maxi,那么跑一个循环,每次该星球如果人数<maxi,那么能加倍就加倍到离maxi最近的位置,然后计算他们的差,比如2 1035,加倍后为1024 1035,差为11,那么到时候1024减到11的时候,1035变成了2

1093. Count PAT&#39;s (25)想法题吧,算是排列组合吧

1093. Count PAT's (25) 时间限制 120 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CAO, Peng The string APPAPT contains two PAT's as substrings. The first one is formed by the 2nd, the 4th, and the 6th characters, and the second one is formed by the 3r

HDU 2410 Barbara Bennett&amp;#39;s Wild Numbers (想法题)

题目链接:HDU 2410 Barbara Bennett's Wild Numbers 题意:给出两串数w,s(长度同样),第一串中有"?",问"?"取的值使w相应的数大于s相应的数 的最慷慨案数. 思路:W,S一一相应比較: 遇到第一个'?'之前比較情况 1.w[i]<s[i] 方案数0种:break: 2.w[i]>s[i] break. 之后有n个''?' 方案数就有10的n次方种. 3.w[i]=s[i] 继续比較.反复1.2两个条件. 遇到'

HDU 4972 Bisharp and Charizard 想法题

Bisharp and Charizard Time Limit: 1 Sec  Memory Limit: 256 MB Description Dragon is watching NBA. He loves James and Miami Heat. Here's an introduction of basketball game:http://en.wikipedia.org/wiki/Basketball. However the game in Dragon's version i

HDU - 5806 NanoApe Loves Sequence Ⅱ 想法题

http://acm.hdu.edu.cn/showproblem.php?pid=5806 题意:给你一个n元素序列,求第k大的数大于等于m的子序列的个数. 题解:题目要求很奇怪,很多头绪但写不出,选择跳过的题,简称想法题. 首先考虑区间的更新方法:区间左端l不动,右端r滑动, 滑到有k个数>=m时,此区间符合条件,并且发现右端点再往右滑到底,此条件一直符合(因为若加入的数小于"第K大的数",则毫无影响.若不然,加入该数会产生一个新的第k大数,保证>="第K大

hdu 5178想法题

比赛时,想法是:固定位置b,然后在b前面找大小处于[x[b] - k, x[b] + k]的数字个数,最后在加起来就是结果,但是要动态的维护b之前的数自动从小到大排序,每次都会新添加一个数,可以用二分插入,但是往后移动的数太多,直接就tle了. 后来的想法是:把原题等价转化一下,先把数据排序,然后对于当前位置i,找到左边的第一个>= x[i] - k的位置pos,则结果就是所有i - pos 的和. 原因是:排序后,x[b] - x[a] <= k, b > a. 讨论:b未排序的位置

POJ 1066 Treasure Hunt [想法题]

题目链接: http://poj.org/problem?id=1066 -------------------------------------------------------------------------------------------------------- 这题刚看后可能会去纠结如何建图后进行最短路 不过这样做不仅代码会复杂许多 还有可能出现些不好判断的部分 不过再多想一下我们可以发现如下性质 如果起点和终点位于某条障碍线段的两侧 那么这条线段有且仅有一次被穿过 所以只