zoukankan      html  css  js  c++  java
  • codeforces 1103E清华集训 石家庄的工人阶级队伍比较坚强

    希望复习高进制FWT的时候,能够快速回想起来。
    FWT感觉就是每一维单独考虑,(虽然我不知道为什么这是对的)
    分别对一个奇怪的东西做卷积,
    那个奇怪的东西在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)<<'
    ';
    }
    
  • 相关阅读:
    零售数据框架
    API安全Checklist
    高级区块链工程师评定
    软件项目复杂性
    e-Commerce电商参考云架构
    面试中的学习能力判断
    SpringCloud微服务架构案例-共享服务中心
    Software Architecture and High Level Design软件架构与概要设计
    基于Istio的ServiceMesh
    a store with that domain name already exists怎么解决
  • 原文地址:https://www.cnblogs.com/Yuhuger/p/10421094.html
Copyright © 2011-2022 走看看