北京集训TEST16——图片加密(fft+kmp)

Posted AseanA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了北京集训TEST16——图片加密(fft+kmp)相关的知识,希望对你有一定的参考价值。

题目:

Description

      CJB天天要跟妹子聊天,可是他对微信的加密算法表示担心:“微信这种加密算法,早就过时了,我发明的加密算法早已风靡全球,安全性天下第一!”

      CJB是这样加密的:设CJB想加密的信息有 m 个字节。首先,从网上抓来一张 n(nm) 个字节的图片,分析里面的每个字节(byte)。每个字节有8位(bit)二进制数字。他想替换掉某些字节中最低位的二进制数字,使得这张图片中,连续 m 个字节恰为他想加密的信息。这样,图片看起来没什么区别,却包含了意味深长的信息。

      很显然,不是所有的图片都能让CJB加密他那 m 个字节的信息。他想请你帮忙写个程序判断这张图片是否能加密指定的信息。如果可以加密,则CJB要求改变最少的字节数。如果仍有多种解,他希望信息在图片中的位置越前越好。

Input

      第一行包含两个整数 n,m(1n,m250000) ,表示图片的大小和信息的大小。

      第二行表示图片的内容。

      第三行表示信息的内容。

      所有内容都以二进制字节的形式给出。每个字节有8位,最左边是最高位,最右边为最低位。

Output

      如果这张图片不能加密这些信息,输出No

      否则第一行输出Yes,第二行输出两个整数:最少修改的字节数,加密信息的起始位置。如果有多组解,要求加密信息的起始位置尽量前。

Sample Input

【样例输入1】
3 2
11110001 11110001 11110000
11110000 11110000
【样例输入2】
3 1 
11110000 11110001 11110000
11110000

Sample Output

【样例输出1】
Yes
1 2
【样例输出2】
Yes
0 1

HINT

 

【样例解释】

      图片有3个字节,信息有2个字节。

      图片前两个字节可以匹配信息,需要改变两个字节中的最低位。

      图片后两个字节可以匹配信息,只需要改变一个字节(第二个字节)中的最低位,信息在图片中的起始位置为第2个字节。

【数据范围与约定】

      对于10%的数据, n,m500

      对于40%的数据, n,m5000

      对于70%的数据, n,m105

      对于所有数据, 1n,m2.5×105


 

题解:

本题解法:KMP+FFT。

首先,先去掉最低位,跑一次KMP,记录所有可以加密为信息的起始位置。

然后我们只保留最低位,存入a, b数组里。我们很容易发现,从第 i 个字节作为起始位置计算的话,答案为

 

j=1ma[i+j] xor b[j]

 

我们可以把b反过来,即可变成卷积形式。

其中,异或可以拆成两个乘法操作相加,即:a xor b=(a?!b)+(!a?b) ,那么我们做两次FFT就可以解决了~

心得:

  又一道fft求卷积题····表示做了这么多次fft终于有点感觉了,其实以后做题要是推出了带∑的式子应该快点想到fft的··只要想办法把里面的运算变成相乘的就行了···

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=250005;
const double pi=acos(-1.0);
int a[N],b[N],c[N],d[N];
int n,m;
int able[N],tot,rev[N];
int nxt[N],k,xj;
int ansa[N],ansb[N];
char s[N];
struct Complex {
    double r,i;
    Complex (double r=0,double i=0):r(r),i(i) {}
} A[N],C[N],B[N],D[N];
Complex operator + (Complex &a,Complex &b) {
    return Complex(a.r+b.r,a.i+b.i);
}
Complex operator - (Complex &a,Complex &b) {
    return Complex(a.r-b.r,a.i-b.i);
}
Complex operator * (Complex &a,Complex &b) {
    return Complex(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r);
}
Complex operator / (Complex &a,double x) {
    return Complex(a.r/x,a.i/x);
}
inline void kmp()
{
   for (int i=2,j=0;i<=m;++i) {
        while (j && b[i]!=b[j+1]) j=nxt[j];
        nxt[i]=(b[i]==b[j+1]?++j:j);
    }
    for (int i=1,j=0;i<=n;++i) {
        while (j && a[i]!=b[j+1]) j=nxt[j];
        if (a[i]==b[j+1]) ++j;
        if (j==m) able[++tot]=i-m+1,j=nxt[j];
    }
}
inline void pre()
{
  for (k=1,xj=0;k<=(n<<1);k<<=1,++xj);
    for (int i=0;i<k;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(xj-1));
}
void fft(Complex a[],int len,int op=1) {
    for (int i=0;i<len;++i) if (i<rev[i]) swap(a[i],a[rev[i]]);
    for (int i=2;i<=len;i<<=1) {
        Complex wn(cos(pi/i),sin(pi/i)*op);
        for (int j=0;j<len;j+=i) {
            Complex w(1);
            for (int k=j;k<j+i/2;++k,w=w*wn) {
                Complex u=a[k],v=a[k+i/2]*w;
                a[k]=u+v,a[k+i/2]=u-v;
            }
        }
    }
    if (op==-1) {
        for (int i=0;i<len;++i) a[i]=a[i]/len;
    }
}
int main()
{
  //freopen("a.in","r",stdin);
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
  for(int i=1;i<=m;i++)
    scanf("%d",&b[i]);
  for(int i=1;i<=n;i++)
    c[i]=a[i]%10;
  for(int i=1;i<+m;i++)
    d[i]=b[i]%10;
  for(int i=1;i<=n;i++)
    a[i]=a[i]/10;
  for(int i=1;i<=m;i++)
    b[i]=b[i]/10;
  kmp();
  if(!tot)
  {
    cout<<"No"<<endl;
    return 0;
  }
  else
    cout<<"Yes"<<endl;
  pre();
    for (int i=0;i<k;++i) A[i]=B[i]=Complex();
    for (int i=1;i<=n;++i) A[i]=c[i];
    for (int i=1;i<=m;++i) B[i]=!d[m-i+1];
    fft(A,k);
    fft(B,k);
    for (int i=0;i<k;++i) A[i]=A[i]*B[i];
    fft(A,k,-1);
    for (int i=m+1;i<=n+1;++i) ansa[i-m]=(int)(A[i].r+0.5);
     
    for (int i=0;i<k;++i) A[i]=B[i]=Complex();
    for (int i=1;i<=n;++i) A[i]=!c[i];
    for (int i=1;i<=m;++i) B[i]=d[m-i+1];
    fft(A,k);
    fft(B,k);
    for (int i=0;i<k;++i) A[i]=A[i]*B[i];
    fft(A,k,-1);
    for (int i=m+1;i<=n+1;++i) ansb[i-m]=(int)(A[i].r+0.5);   
  
  for(int i=1;i<=n-m+1;i++)
    ansa[i]+=ansb[i];
  
  int anspos,minchange=1e+9;
  for(int i=1;i<=tot;i++)
  {
    if(minchange>ansa[able[i]])
    {
      minchange=ansa[able[i]];
      anspos=able[i];
    }
  }
  cout<<minchange<<" "<<anspos<<endl;
  return 0;
}

 

 

 

以上是关于北京集训TEST16——图片加密(fft+kmp)的主要内容,如果未能解决你的问题,请参考以下文章

北京集训TEST13——PA(Goodness)

AHOI2019短期集训

[北京集训测试赛]灯(Light)-奇怪乱搞数学题-素数

[2018.6.22集训]admirable-拆系数FFT-多项式相关

2021年SWPUACM暑假集训day4KMP算法

学习总结——北京集训