codeforces 1103E清华集训 石家庄的工人阶级队伍比较坚强

Posted yuhuger

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforces 1103E清华集训 石家庄的工人阶级队伍比较坚强相关的知识,希望对你有一定的参考价值。

希望复习高进制FWT的时候,能够快速回想起来。
FWT感觉就是每一维单独考虑,(虽然我不知道为什么这是对的)
分别对一个奇怪的东西做DFT,
那个奇怪的东西在k进制下就是关于k次单位根的范德蒙特矩阵。
范德蒙特矩阵的逆矩阵大致就是每行除了第一个数之外翻转一下,然后除以矩阵的阶。
也可理解为原矩阵把k次单位根取反,反正每一维上就是个DFT,按FFT的做法搞就行了。

然后这两道题毒瘤的地方就在于不能用实数来表示一个复数,否则会爆精。
而且你也不一定可以算出单位根的值,十分毒瘤。
于是你就需要搞事,
就是把一个数用向量表示,表示单位根的某些次幂前的系数。
单位根的性质 https://blog.csdn.net/DT_Kang/article/details/79944113
分圆多项式 https://yhx-12243.github.io/OI-transit/memos/17.html#pr-1-1
可以证明在模分圆多项式的意义下对答案没有影响,虽然我也不会证。

然后考虑清华集训这题。
(k=3)时,分圆多项式为(x^2+x+1)
并且把(w^1_3)带进去由分圆多项式的定义可得(w^1_3+w^2_3+1=0)
然后你就只需要存两个系数了。

有一个优化是DFT时乘上的是单位根,所以可以不用乘法,直接移系数。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
namespace io {
  const int SIZE = 1.1e6;
  char buff[SIZE];
  char *l = buff, *r = buff;
  void init() {
    l = buff;
    r = l + fread(buff, 1, SIZE, stdin);
  }
  char gc() {
    if (l == r) init();
    if(l==r)return EOF;
      return *(l++);
  }
  void read(int &x) {
    x = 0;
    char ch = gc();
    while(!isdigit(ch)) ch = gc();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
  }
}
using io::read;
namespace{
    int M;
    void FIX(int &x){
        x>=M?x-=M:(x<0?x+=M:0);
    }
    int ADD(int x,int y){
        return (x+=y)>=M?x-M:x;
    }
    int SUB(int x,int y){
        return (x-=y)<0?x+M:x;
    }
    void ADDT(int &x,int y){
        (x+=y)>=M?x-=M:0;
    }
    void SUBT(int &x,int y){
        (x-=y)<0?x+=M:0;
    }
    int MUL(int x,int y){
        return (ll)x*y%M;//(ll)x*y-(ll)x*y/M*M
    }
    int fp(int x,int y){
        int ret=1;
        for (; y; y>>=1,x=MUL(x,x))
            if (y&1) ret=MUL(ret,x);
        return ret;
    }
    void exgcd(int a,int b,int &x,int &y){
        if (b==0) return (void)(x=1,y=0);
        exgcd(b,a%b,y,x);
        y-=a/b*x;
    }
}
template<int N>
struct Number{
    int a[N-1];
    Number(){
        memset(a,0,sizeof(a));
    }
    friend ostream & operator <<(ostream &out,const Number<N> &b){
        out<<'{';
        for (int i=0; i<N-2; ++i) out<<b.a[i]<<',';
        return out<<b.a[N-2]<<'}';
    }
    Number operator +(const Number &b) const{
        Number ret;
        for (int i=0; i<N-1; ++i) ret.a[i]=ADD(a[i],b.a[i]);
        return ret;
    }
    Number operator -(const Number &b) const{
        Number ret;
        for (int i=0; i<N-1; ++i) ret.a[i]=SUB(a[i],b.a[i]);
        return ret;
    }
    Number operator *(const Number &b) const{
        Number ret;
        int tmp=0;
        for (int i=0; i<N-1; ++i)
            for (int j=0; j<N-1; ++j)
                ADDT(i+j==N-1?tmp:ret.a[i+j>=N?i+j-N:i+j],MUL(a[i],b.a[j]));
        for (int i=0; i<N-1; ++i) SUBT(ret.a[i],tmp);
        return ret;
    }
    bool operator <(const Number &b) const{
        for (int i=0; i<N-1; ++i) if (a[i]!=b.a[i]) return a[i]<b.a[i];
        return 0;
    }
    Number operator <<(const int &c) const{
        //less than N
        Number ret;
        int tmp=0;
        for (int i=0; i<N-1; ++i) ADDT(i+c==N-1?tmp:ret.a[i+c>=N?i+c-N:i+c],a[i]);
        for (int i=0; i<N-1; ++i) SUBT(ret.a[i],tmp);
        return ret;
    }
};
namespace std{
    template<int N>
    struct hash<Number<N> >{
        int operator()(const Number<N> &a) const{
            int ret=0;
            for (int i=0; i<N-1; ++i) ret^=std::hash<int>{}(a.a[i]);
            return ret;
        }
    };
    template<int N>
    struct equal_to<Number<N> >{
        bool operator()(const Number<N> &a,const Number<N> &b) const{
            for (int i=0; i<N-1; ++i) if (a.a[i]!=b.a[i]) return 0;
            return 1;
        }
    };
}
unordered_map<Number<3>,Number<3> > mp;
template<int N>
Number<N> fp(Number<N> x,int y){
    Number<N> ret;
    ret.a[0]=1;
    for (; y; y>>=1,x=x*x) if (y&1) ret=ret*x;
    return ret;
}
template<int N,int LEN>
struct FWT_helper{
    typedef Number<N> Num;
    Num w[N][N];
    int pwd[LEN+1];
    FWT_helper(){
        assert(N>1);
        assert(M);
        for (int i=0; i<N; ++i) w[i][0].a[0]=1;
        w[0][1].a[0]=1;
        w[1][1].a[1]=1;
        for (int i=2; i<N; ++i) w[i][1]=w[i-1][1]*w[1][1];
        for (int i=0; i<N; ++i)
            for (int j=2; j<N; ++j)
                w[i][j]=w[i][j-1]*w[i][1];
        pwd[0]=1;
        for (int i=1; i<=LEN; ++i) pwd[i]=pwd[i-1]*N;
    }
    void DFT(Num *a){
        Num b[N];
        for (int i=0; i<N; ++i){
            //for (int j=0; j<N; ++j) b[i]=b[i]+a[j]*w[i][j];???
            for (int j=0,d=0; j<N; ++j,(d+=i)>=N?d-=N:0)
                b[i]=b[i]+(a[j]<<d);
        }
        for (int i=0; i<N; ++i) a[i]=b[i];
    }
    void IDFT(Num *a){
        reverse(a+1,a+N);
        DFT(a);
//Don't forget to div
    }
    void FWT(Num *a,int m,bool rev){
        for (int i=0; i<m; ++i)
            for (int cj=0; cj<pwd[m]; cj+=pwd[i+1])
                for (int j=cj; j<cj+pwd[i]; ++j){
                    Num tmp[N];
                    for (int k=0; k<N; ++k) tmp[k]=a[j+pwd[i]*k];
                    if (rev) IDFT(tmp);
                    else DFT(tmp);
                    for (int k=0; k<N; ++k) a[j+pwd[i]*k]=tmp[k];
                }
        if (rev){
            int inv,tmp;
            exgcd(pwd[m],M,inv,tmp);
            FIX(inv);
            for (int i=0; i<pwd[m]; ++i) a[i].a[0]=MUL(a[i].a[0],inv);
        }
    }
};
void test(){
    M=998244353;
    FWT_helper<3,15> a;
    Number<3> done[9];
    for (int i=0; i<a.pwd[2]; ++i) done[i].a[0]=i;
    a.FWT(done,2,0);
    for (int i=0; i<a.pwd[2]; ++i) done[i]=done[i]*done[i];
    a.FWT(done,2,1);
    for (int i=0; i<a.pwd[2]; ++i) cerr<<done[i]<<" "; cerr<<endl;
}
int m,t;
int cnt1[531441],cnt2[531441];
Number<3> f[531441],g[531441];
int b[13][13];
int main(){
    //test();
    read(m); read(t); read(M);
    FWT_helper<3,12> ups;
    for (int i=0; i<ups.pwd[m]; ++i){
        int x; read(x);
        f[i].a[0]=x;
    }
    for (int i=0; i<=m; ++i)
        for (int j=0; i+j<=m; ++j)
            read(b[i][j]);
    for (int i=0; i<ups.pwd[m]; ++i){
        cnt1[i]=cnt1[i/3]+(i%3==1);
        cnt2[i]=cnt2[i/3]+(i%3==2);
    }
    for (int i=0; i<ups.pwd[m]; ++i)
        g[i].a[0]=b[cnt1[i]][cnt2[i]];
    ups.FWT(f,m,0);
    ups.FWT(g,m,0);
    for (int i=0; i<ups.pwd[m]; ++i)
        f[i]=f[i]*(mp.count(g[i])?mp[g[i]]:(mp[g[i]]=fp(g[i],t)));
    ups.FWT(f,m,1);
    for (int i=0; i<ups.pwd[m]; ++i) cout<<f[i].a[0]<<'
';
}

然后考虑cf题,朴素想法是直接模(x^9+x^8+x^7+x^6+x^5+x^4+x^3+x^2+x+1),最后再模10的分圆多项式,这样也可以过。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace io {
  const int SIZE = 1.1e6;
  char buff[SIZE];
  char *l = buff, *r = buff;
  void init() {
    l = buff;
    r = l + fread(buff, 1, SIZE, stdin);
  }
  char gc() {
    if (l == r) init();
    if(l==r)return EOF;
      return *(l++);
  }
  void read(int &x) {
    x = 0;
    char ch = gc();
    while(!isdigit(ch)) ch = gc();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
  }
}
using io::read;
namespace{
    ull ADD(ull x,ull y){
        return x+y;
    }
    void ADDT(ull &x,ull y){
        x+=y;
    }
    ull SUB(ull x,ull y){
        return x-y;
    }
    void SUBT(ull &x,ull y){
        x-=y;
    }
    ull MUL(ull x,ull y){
        return x*y;
    }
}
template<int N>
struct Number{
    ull a[N-1];
    Number(){
        memset(a,0,sizeof(a));
    }
    friend ostream & operator <<(ostream &out,const Number<N> &b){
        out<<'{';
        for (int i=0; i<N-2; ++i) out<<b.a[i]<<',';
        return out<<b.a[N-2]<<'}';
    }
    Number operator +(const Number &b) const{
        Number ret;
        for (int i=0; i<N-1; ++i) ret.a[i]=ADD(a[i],b.a[i]);
        return ret;
    }
    Number operator -(const Number &b) const{
        Number ret;
        for (int i=0; i<N-1; ++i) ret.a[i]=SUB(a[i],b.a[i]);
        return ret;
    }
    Number operator *(const Number &b) const{
        Number ret;
        ull tmp=0;
        for (int i=0; i<N-1; ++i)
            for (int j=0; j<N-1; ++j)
                ADDT(i+j==N-1?tmp:ret.a[i+j>=N?i+j-N:i+j],MUL(a[i],b.a[j]));
        for (int i=0; i<N-1; ++i) SUBT(ret.a[i],tmp);
        return ret;
    }
    bool operator <(const Number &b) const{
        for (int i=0; i<N-1; ++i) if (a[i]!=b.a[i]) return a[i]<b.a[i];
        return 0;
    }
    Number operator <<(const int &c) const{
        //less than N
        Number ret;
        ull tmp=0;
        for (int i=0; i<N-1; ++i) ADDT(i+c==N-1?tmp:ret.a[i+c>=N?i+c-N:i+c],a[i]);
        for (int i=0; i<N-1; ++i) SUBT(ret.a[i],tmp);
        return ret;
    }
};
template<int N>
Number<N> fp(Number<N> x,int y){
    Number<N> ret;
    ret.a[0]=1;
    for (; y; y>>=1,x=x*x) if (y&1) ret=ret*x;
    return ret;
}
const ull inv5=14757395258967641293ull;
template<int N,int LEN>
struct FWT_helper{
    typedef Number<N> Num;
    Num w[N][N];
    int pwd[LEN+1];
    FWT_helper(){
        assert(N>1);
        for (int i=0; i<N; ++i) w[i][0].a[0]=1;
        w[0][1].a[0]=1;
        w[1][1].a[1]=1;
        for (int i=2; i<N; ++i) w[i][1]=w[i-1][1]*w[1][1];
        for (int i=0; i<N; ++i)
            for (int j=2; j<N; ++j)
                w[i][j]=w[i][j-1]*w[i][1];
        pwd[0]=1;
        for (int i=1; i<=LEN; ++i) pwd[i]=pwd[i-1]*N;
    }
    void DFT(Num *a){
        Num b[N];
        for (int i=0; i<N; ++i){
            //for (int j=0; j<N; ++j) b[i]=b[i]+a[j]*w[i][j];???
            for (int j=0,d=0; j<N; ++j,(d+=i)>=N?d-=N:0)
                b[i]=b[i]+(a[j]<<d);
        }
        for (int i=0; i<N; ++i) a[i]=b[i];
    }
    void IDFT(Num *a){
        reverse(a+1,a+N);
        DFT(a);
//Don't forget to div
    }
    void FWT(Num *a,int m,bool rev){
        for (int i=0; i<m; ++i)
            for (int cj=0; cj<pwd[m]; cj+=pwd[i+1])
                for (int j=cj; j<cj+pwd[i]; ++j){
                    Num tmp[N];
                    for (int k=0; k<N; ++k) tmp[k]=a[j+pwd[i]*k];
                    if (rev) IDFT(tmp);
                    else DFT(tmp);
                    for (int k=0; k<N; ++k) a[j+pwd[i]*k]=tmp[k];
                }
    }
};
int n;
Number<10> a[100000];
int main(){
    FWT_helper<10,5> ups;
    read(n);
    for (int i=0; i<n; ++i){
        int x;
        read(x);
        ++a[x].a[0];
    }
    ups.FWT(a,5,0);
    for (int i=0; i<ups.pwd[5]; ++i) a[i]=fp(a[i],n);
    ups.FWT(a,5,1);
    for (int i=0; i<n; ++i){
        //cerr<<a[0]<<" "<<(~0ull)<<endl;
        for (int j=8; j>=7; --j){
            a[i].a[j-5]-=a[i].a[j];
        }
        //a[i].a[3]-=a[i].a[6];
        a[i].a[1]-=a[i].a[6];
        a[i].a[0]-=a[i].a[5];
        a[i].a[3]+=a[i].a[4];
        a[i].a[2]-=a[i].a[4];
        a[i].a[1]+=a[i].a[4];
        a[i].a[0]-=a[i].a[4];
        //cerr<<a[i].a[0]<<endl;
        //exit(0);
    }
    ull inv=inv5*inv5*inv5*inv5*inv5;
    for (int i=0; i<ups.pwd[5]; ++i) a[i].a[0]=MUL(a[i].a[0],inv);
    for (int i=0; i<n; ++i){
        //for (int j=1; j<9; ++j) assert(a[i].a[j]==0);
        cout<<(a[i].a[0]>>5)%(1ll<<58)<<'
';
    }
}

另一种做法是直接模10的分圆多项式,但是比较难写,因为乘法高次项的贡献比较恶心。
然后考虑将10次单位根转化为5次单位根,有(w^5_{10}=-1)
转化了之后直接对长度为4的搞,也可以。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
namespace io {
  const int SIZE = 7e5+10;
  char buff[SIZE];
  char *l = buff, *r = buff;
  void init() {
    l = buff;
    r = l + fread(buff, 1, SIZE, stdin);
  }
  char gc() {
    if (l == r) init();
    if(l==r)return EOF;
      return *(l++);
  }
  void read(int &x) {
    x = 0;
    char ch = gc();
    while(!isdigit(ch)) ch = gc();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
  }
}
using io::read;
typedef unsigned long long ull;
template<int N>
struct Number{
    ull a[N-1];
    Number(){
        memset(a,0,sizeof(a));
    }
    friend ostream & operator <<(ostream &out,const Number<N> &b){
        out<<'{';
        for (int i=0; i<N-2; ++i) out<<b.a[i]<<',';
        return out<<b.a[N-2]<<'}';
    }
    Number operator +(const Number &b) const{
        Number ret;
        for (int i=0; i<N-1; ++i) ret.a[i]=a[i]+b.a[i];
        return ret;
    }
    Number operator -(const Number &b) const{
        Number ret;
        for (int i=0; i<N-1; ++i) ret.a[i]=a[i]-b.a[i];
        return ret;
    }
    Number operator *(const Number &b) const{
        Number ret;
        ull tmp=0;
        for (int i=0; i<N-1; ++i)
            for (int j=0; j<N-1; ++j)
                (i+j==N-1?tmp:ret.a[i+j>=N?i+j-N:i+j])+=a[i]*b.a[j];
        for (int i=0; i<N-1; ++i) ret.a[i]-=tmp;
        return ret;
    }
    bool operator <(const Number &b) const{
        for (int i=0; i<N-1; ++i) if (a[i]!=b.a[i]) return a[i]<b.a[i];
        return 0;
    }
    Number operator <<(const int &c) const{
        //less than N
        Number ret;
        ull tmp=0;
        for (int i=0; i<N-1; ++i) (i+c==N-1?tmp:ret.a[i+c>=N?i+c-N:i+c])+=a[i];
        for (int i=0; i<N-1; ++i) ret.a[i]-=tmp;
        return ret;
    }
};
typedef Number<5> Num;
template<int N>
Number<N> fp(Number<N> x,int y){
    Number<N> ret;
    ret.a[0]=1;
    for (; y; y>>=1,x=x*x) if (y&1) ret=ret*x;
    return ret;
}
void DFT(Num *a){
    Num b[10];
    for (int i=0; i<10; ++i)
        for (int j=0,t=0; j<10; ++j,(t+=i)>=10?t-=10:0){
            if (~t&1) b[i]=b[i]+(a[j]<<(t>>1));
            else b[i]=b[i]-(a[j]<<((t+5)>>1)%5);
        }
    for (int i=0; i<10; ++i) a[i]=b[i];
}

const int pwd[6]={1,10,100,1000,10000,100000};
const ull inv5=14757395258967641293ull;
void FWT(Num *a,bool rev){
    for (int i=0; i<5; ++i){
        for (int cj=0; cj<100000; cj+=pwd[i+1])
            for (int j=cj; j<cj+pwd[i]; ++j){
                Num t[10];
                for (int k=0; k<10; ++k) t[k]=a[j+k*pwd[i]];
                if (rev) reverse(t+1,t+10);
                //for (int k=0; k<10; ++k) cout<<t[k]<<endl;
                DFT(t);
                //for (int k=0; k<10; ++k) cout<<t[k]<<endl;
                //exit(0);
                for (int k=0; k<10; ++k) a[j+k*pwd[i]]=t[k];
            }
    }
    if (rev){
        ull c=inv5*inv5*inv5*inv5*inv5;
        for (int i=0; i<100000; ++i) a[i].a[0]*=c;
    }
}
const int N=100000;
int n;
Num a[N];
int main(){
    read(n);
    for (int i=0; i<n; ++i){
        int x; read(x);
        ++a[x].a[0];
    }
    FWT(a,0);
    for (int i=0; i<100000; ++i) a[i]=fp(a[i],n);
    FWT(a,1);
    for (int i=0; i<n; ++i)
        cout<<(a[i].a[0]>>5)%(1ll<<58)<<'
';
}

以上是关于codeforces 1103E清华集训 石家庄的工人阶级队伍比较坚强的主要内容,如果未能解决你的问题,请参考以下文章

UOJ#272. 清华集训2016石家庄的工人阶级队伍比较坚强

清华集训2014 做题记录

2017.11.26清华集训2017模拟

201803182018北京集训测试赛

清华集训2015 V

清华集训2014 玄学