zoukankan      html  css  js  c++  java
  • HAOI2018简要题解

    大概之后可能会重写一下,写的详细一些?

    Day 1

    T1 简单的背包:DP

    分析

    可以发现,如果选出了一些数,令这些数的(gcd)(d),那么这些数能且仅能组合成(gcd(d,P))的所有倍数。

    然后就可以DP了,令(f[i][j])表示考虑了前(i)个数,所有选出的数和(P)(gcd)(j)的方案数,有状态转移方程:

    [f[i+1][j]+=f[i][j] ]

    [f[i+1][gcd(gcd(V_{i+1},P),j)]+=f[i][j] ]

    (这里考虑的是从一个状态可以转移到哪些状态。)

    这是一个(O(nP))的DP,我们发现(P)的约数很少,(f)的第二维只要考虑(P)的所有约数就好了。然后我们还可以开个桶记录(P)的每个约数在(gcd(V_i,P))中出现了多少次,然后DP式子就变成了:

    [f[i+1][j]+=f[i][j] ]

    [f[i+1][gcd(D_{i+1},j)]+=f[i][j] imes (2^{cnt[i+1]}-1) ]

    这里(f[i][j])的意思是考虑了(P)的前(i)个约数,所有选出的数的(gcd)(j)的方案数。

    时间复杂度大约为(O((n+q) log P+P^{frac{2}{3}}))

    代码

    #include <bits/stdc++.h>
    
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=1e6+5;
    const int MOD=1e9+7;
    const int HMOD=3e6-1;
    
    int n,q,p,dcnt;
    int d[2005],cnt[2005],pw2[MAXN];
    int f[2005][2005],g[2005];
    
    struct hash_map{
    	int siz,head[HMOD+5],nxt[2005];
    	LL val[2005];int to[2005];
    	inline void insert(LL x,int y){
    		++siz;
    		nxt[siz]=head[x%HMOD];
    		val[siz]=x;
    		to[siz]=y;
    		head[x%HMOD]=siz;
    	}
    	inline int operator [] (LL x){
    		for(register int i=head[x%HMOD];i;i=nxt[i])
    			if(val[i]==x)return to[i];
    		return -1;
    	}
    }mp;
    
    inline int gcd(int x,int y){
    	if(!x||!y)return x+y;
    	while(y){std::swap(x,y),y%=x;}
    	return x;
    }
    
    int main(){
    	n=read(),q=read(),p=read();
    	int lim=sqrt(p)+0.5;
    	d[++dcnt]=0;
    	rin(i,1,lim){
    		if(p%i)continue;
    		d[++dcnt]=i;
    		if(i*i==p)break;
    		d[++dcnt]=p/i;
    	}
    	std::sort(d+1,d+dcnt+1);
    	rin(i,1,dcnt)mp.insert(d[i],i);
    	rin(i,1,n)++cnt[mp[gcd(read(),p)]];
    	pw2[0]=1;rin(i,1,n)pw2[i]=pw2[i-1]*2%MOD;
    	f[1][1]=1;
    	rin(i,1,dcnt){
    		rin(j,1,dcnt){
    			int k=mp[gcd(d[i+1],d[j])];
    			f[i+1][j]=(f[i+1][j]+f[i][j])%MOD;
    			f[i+1][k]=(f[i+1][k]+1ll*f[i][j]*(pw2[cnt[i+1]]-1+MOD))%MOD;
    		}
    	}
    	rin(i,2,dcnt)rin(j,2,i)if(d[i]%d[j]==0)g[i]=(g[i]+f[dcnt][j])%MOD;
    	while(q--)printf("%d
    ",g[mp[gcd(read(),p)]]);
    	return 0;
    }
    

    T2 反色游戏:Tarjan+异或方程组

    分析

    对于一个连通块,若有奇数个黑点,显然无解,否则显然有解,解的个数为(2^{m-n+1})

    用Tarjan求割点随便搞搞就好了。

    代码

    #include <bits/stdc++.h>
    
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=1e5+5;
    const int MOD=1e9+7;
    
    int n,m,ecnt,head[MAXN],deg[MAXN];
    int cnt,siz[MAXN],root[MAXN],blg[MAXN];
    int dcnt,totsiz,dfn[MAXN],low[MAXN],cut[MAXN];
    int pw2[MAXN],notzero[MAXN],pre[MAXN],suf[MAXN];
    char s[MAXN];
    bool vis[MAXN];
    
    struct Edge{
    	int to,nxt;
    }e[MAXN<<1];
    
    inline void add_edge(int bg,int ed){
    	++ecnt;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    }
    
    void dfs(int x){
    	vis[x]=true;blg[x]=cnt;
    	siz[x]=(s[x]=='1');
    	trav(i,x){
    		int ver=e[i].to;if(vis[ver])continue;
    		dfs(ver);siz[x]+=siz[ver];
    	}
    }
    
    void tarjan(int x){
    	dfn[x]=low[x]=++dcnt;
    	int sum=(s[x]=='1');
    	trav(i,x){
    		int ver=e[i].to;
    		if(!dfn[ver]){
    			tarjan(ver);
    			low[x]=std::min(low[x],low[ver]);
    			if(low[ver]>=dfn[x]){
    				sum+=siz[ver];++cut[x];
    				notzero[x]&=((siz[ver]&1)==0);
    			}
    		}
    		else low[x]=std::min(low[x],dfn[ver]);
    	}
    	notzero[x]&=(((totsiz-sum)&1)==0);
    }
    
    void clear(){
    	ecnt=cnt=dcnt=0;
    	memset(head,0,sizeof head);
    	memset(deg,0,sizeof deg);
    	memset(siz,0,sizeof siz);
    	memset(dfn,0,sizeof dfn);
    	memset(cut,0,sizeof cut);
    	memset(vis,false,sizeof vis);
    }
    
    int main(){
    	int T=read();n=100000;
    	pw2[0]=1;rin(i,1,n)pw2[i]=pw2[i-1]*2%MOD;
    	while(T--){
    		clear();
    		n=read(),m=read();
    		rin(i,1,n)notzero[i]=1;
    		rin(i,1,m){
    			int u=read(),v=read();
    			add_edge(u,v),add_edge(v,u);
    			++deg[u],++deg[v];
    		}
    		scanf("%s",s+1);
    		rin(i,1,n)if(!vis[i])dfs(root[++cnt]=i);
    		rin(i,1,cnt)--cut[root[i]],totsiz=siz[root[i]],tarjan(root[i]);
    		pre[0]=1;rin(i,1,cnt)pre[i]=(pre[i-1]&((siz[root[i]]&1)==0));
    		suf[cnt+1]=1;irin(i,cnt,1)suf[i]=(suf[i+1]&((siz[root[i]]&1)==0));
    		printf("%d ",pre[cnt]*pw2[m-n+cnt]);
    		rin(i,1,n)printf("%d ",pre[blg[i]-1]*suf[blg[i]+1]*notzero[i]*pw2[(m-deg[i])-(n-1)+cnt+cut[i]]);
    		putchar('
    ');
    	}
    	return 0;
    }
    

    T3 字串覆盖:后缀自动机+启发式合并+倍增+数据分治

    分析

    使用set+启发式合并,维护right集合,把所有询问离线挂在parent树上。

    对于(r-l geq 51)的询问暴力找下一个出现的位置,(r-l < 51)的预处理倍增数组后快速查询就好了。

    代码

    #include <bits/stdc++.h>
    
    #define rin(i,a,b) for(int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(int i=(a);i>=(b);--i)
    #define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    #define mkpr std::make_pair
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=100005;
    
    int n,K,m,las,tot;
    int ecnt,head[MAXN<<1];
    LL ans[MAXN];
    char A[MAXN],B[MAXN];
    std::set<int> right[MAXN<<1];
    
    struct sam{
    	int fa,to[26],len;
    }a[MAXN<<1];
    
    void extend(int c,int posi){
    	int p=las,np=++tot;las=np;right[np].insert(posi);
    	a[np].len=a[p].len+1;
    	while(p&&!a[p].to[c])a[p].to[c]=np,p=a[p].fa;
    	if(!p){a[np].fa=1;return;}
    	int q=a[p].to[c];
    	if(a[p].len+1==a[q].len){a[np].fa=q;return;}
    	int nq=++tot;a[nq]=a[q];
    	a[nq].len=a[p].len+1,a[np].fa=a[q].fa=nq;
    	while(p&&a[p].to[c]==q)a[p].to[c]=nq,p=a[p].fa;
    }
    
    struct Edge{
    	int to,nxt;
    }e[MAXN<<1];
    
    inline void add_edge(int bg,int ed){
    	++ecnt;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    }
    
    int anc[MAXN<<1][20];
    
    void calc_anc(){
    	rin(i,1,tot)anc[i][0]=a[i].fa;
    	rin(i,1,18)rin(j,1,tot)anc[j][i]=anc[anc[j][i-1]][i-1];
    }
    
    inline int climb(int x,int lent){
    	irin(i,18,0)if(anc[x][i]&&a[anc[x][i]].len>=lent)x=anc[x][i];
    	return x;
    }
    
    int pos[MAXN],len[MAXN];
    
    void run_sam(){
    	int now=1,nowlen=0;
    	rin(i,1,n){
    		while(now&&!a[now].to[B[i]]){
    			now=a[now].fa;
    			nowlen=a[now].len;
    		}
    		if(!now)now=1,nowlen=0;
    		else{
    			now=a[now].to[B[i]];
    			++nowlen;
    		}
    		pos[i]=now,len[i]=nowlen;
    	}
    }
    
    struct quest{
    	int s,t,len,id;
    };
    
    std::vector<quest> qst[MAXN<<1];
    
    typedef std::set<int>::iterator iter;
    
    int qtot;
    
    struct quest2{
    	int s,t,len,pos,id;
    	inline friend bool operator < (quest2 x,quest2 y){
    		return x.pos==y.pos?x.len<y.len:x.pos<y.pos;
    	}
    }q2[MAXN];
    
    int ptot,begpos[MAXN],nxt[MAXN][20];
    LL sum[MAXN][20];
    
    void calc_nxt(int posi,int lent){
    	rin(i,1,ptot)memset(nxt[i],0,sizeof nxt[i]),memset(sum[i],0,sizeof sum[i]);
    	ptot=0;
    	for(iter it=right[posi].begin();it!=right[posi].end();++it)begpos[++ptot]=*it-lent+1;
    	rin(i,1,ptot)nxt[i][0]=std::lower_bound(begpos+1,begpos+ptot+1,begpos[i]+lent)-begpos,sum[i][0]=K-begpos[i];
    	rin(i,1,18)rin(j,1,ptot)nxt[j][i]=nxt[nxt[j][i-1]][i-1],sum[j][i]=sum[j][i-1]+sum[nxt[j][i-1]][i-1];
    }
    
    inline LL calc_ans(int s,int t,int lent){
    	int now=std::lower_bound(begpos+1,begpos+ptot+1,s)-begpos;LL ret=0;
    	irin(i,18,0)if(nxt[now][i]>=1&&nxt[now][i]<=ptot&&begpos[nxt[now][i]]+lent-1<=t)ret+=sum[now][i],now=nxt[now][i];
    	if(begpos[now]+lent-1<=t)ret+=sum[now][0];
    	return ret;
    }
    
    inline void merge(std::set<int> &x,std::set<int> &y){
    	if(Size(x)<Size(y)){	
    		for(iter it=x.begin();it!=x.end();++it)y.insert(*it);
    		x.swap(y);
    	}
    	else for(iter it=y.begin();it!=y.end();++it)x.insert(*it);
    }
    
    void dfs(int x){
    	trav(i,x){
    		int ver=e[i].to;dfs(ver);
    		merge(right[x],right[ver]);
    	}
    	rin(i,0,Size(qst[x])-1){
    		int now=qst[x][i].s+qst[x][i].len-1;
    		while(1){
    			iter it=right[x].lower_bound(now);
    			if(it==right[x].end()||*it>qst[x][i].t)break;
    			ans[qst[x][i].id]+=K-(*it-qst[x][i].len+1);
    			now=*it+qst[x][i].len;
    		}
    	}
    	int bg=std::lower_bound(q2+1,q2+qtot+1,(quest2){0,0,0,x,0})-q2;
    	rin(i,bg,qtot){
    		if(q2[i].pos!=x)break;
    		if(q2[i].pos!=q2[i-1].pos||q2[i].len!=q2[i-1].len)calc_nxt(q2[i].pos,q2[i].len);
    		ans[q2[i].id]=calc_ans(q2[i].s,q2[i].t,q2[i].len);
    	}
    }
    
    int main(){
    	n=read(),K=read();las=tot=1;
    	scanf("%s",A+1);getchar();
    	scanf("%s",B+1);
    	rin(i,1,n){
    		A[i]-='a',B[i]-='a';		
    		extend(A[i],i);
    	}
    	rin(i,2,tot)add_edge(a[i].fa,i);
    	calc_anc();run_sam();
    	m=read();
    	rin(i,1,m){
    		int s=read(),t=read(),l=read(),r=read();
    		if(r-l+1>len[r]){ans[i]=0;continue;}
    		else if(r-l+1>=52){
    			int ret=climb(pos[r],r-l+1);
    			qst[ret].pb((quest){s,t,r-l+1,i});
    		}
    		else{
    			int ret=climb(pos[r],r-l+1);
    			q2[++qtot]=(quest2){s,t,r-l+1,ret,i};
    		}
    	}
    	std::sort(q2+1,q2+qtot+1);
    	dfs(1);
    	rin(i,1,m)printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    Day 2

    T1 苹果树:组合数学

    分析

    https://blog.csdn.net/Icefox_zhx/article/details/80709753

    代码

    #include <bits/stdc++.h>
    
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=2005;
    
    int n;
    LL p,fac[MAXN],c[MAXN][MAXN];
    
    int main(){
    	n=read(),p=read();
    	fac[0]=1;rin(i,1,n)fac[i]=fac[i-1]*i%p;
    	c[0][0]=1;
    	rin(i,1,n){
    		rin(j,0,i){
    			c[i][j]=c[i-1][j];
    			if(j)c[i][j]=(c[i][j]+c[i-1][j-1])%p;
    		}
    	}
    	LL ans=0;
    	rin(i,2,n)rin(j,1,n-i+1)ans=(ans+1ll*j*(n-j)%p*fac[n-i]%p*j%p*c[n-j-1][i-2]%p*fac[i])%p;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    T2 染色:组合数学+二项式反演+NTT

    分析

    (f(i))表示恰好有(i)种颜色出现了(S)次的方案数,(g(i))表示至少有(i)种颜色出现了(S)次的方案数,有:

    [g(i)=inom{M}{i}inom{N}{iS}frac{(iS)!}{(S!)^i}(M-i)^{N-iS} ]

    [f(i)=sum_{j=i}^{M}inom{j}{i}g(j) ]

    [ans=sum_{i=0}^{M}W_if(i) ]

    二项式反演后化一下式子再NTT就好了。

    代码

    #include <bits/stdc++.h>
    
    #define rin(i,a,b) for(register int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(register int i=(a);i>=(b);--i)
    #define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=1e7+5;
    const int MAXM=1e5+5;
    const int MOD=1004535809;
    const int G=3;
    
    int NTT,INVG,N,M,S,n,m,len,rev[MAXM<<2];
    int w[MAXM<<2],iw[MAXM<<2];
    int A[MAXM<<2],B[MAXM<<2];
    int fac[MAXN],invf[MAXN],W[MAXM];
    
    inline int qpow(int x,int y){
    	if(y<0)return 0;
    	int ret=1,tt=x%MOD;
    	while(y){
    		if(y&1)ret=1ll*ret*tt%MOD;
    		tt=1ll*tt*tt%MOD;
    		y>>=1;
    	}
    	return ret;
    }
    
    void prepare(){
    	for(n=1,len=0;n<=m;n<<=1,++len);
    	rin(i,1,n-1)rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
    }
    
    void ntt(int *c,int dft){
    	rin(i,0,n-1)if(i<rev[i])std::swap(c[i],c[rev[i]]);
    	for(register int mid=1;mid<n;mid<<=1){
    		int r=(mid<<1),u=NTT/r;
    		for(register int l=0;l<n;l+=r){
    			int v=0;
    			for(register int i=0;i<mid;++i,v+=u){
    				int x=c[l+i],y=1ll*c[l+mid+i]*(dft>0?w[v]:iw[v])%MOD;
    				c[l+i]=x+y<MOD?x+y:x+y-MOD;
    				c[l+mid+i]=x-y>=0?x-y:x-y+MOD;
    			}
    		}
    	}
    	if(dft<0){
    		int invn=qpow(n,MOD-2);
    		rin(i,0,n-1)c[i]=1ll*c[i]*invn%MOD;
    	}
    }
    
    inline int c(int n,int m){
    	if(n<0||m<0||n<m)return 0;
    	return 1ll*fac[n]*invf[n-m]%MOD*invf[m]%MOD;
    }
    
    void init(int N,int M){
    	fac[0]=1;rin(i,1,std::max(N,M))fac[i]=1ll*fac[i-1]*i%MOD;
    	invf[std::max(N,M)]=qpow(fac[std::max(N,M)],MOD-2);irin(i,std::max(N,M)-1,0)invf[i]=1ll*invf[i+1]*(i+1)%MOD;
    	for(NTT=1;NTT<=(M<<1);NTT<<=1);
    	INVG=qpow(G,MOD-2);w[0]=iw[0]=1;int v=qpow(G,(MOD-1)/NTT),iv=qpow(INVG,(MOD-1)/NTT);
    	rin(i,1,NTT-1)w[i]=1ll*w[i-1]*v%MOD,iw[i]=1ll*iw[i-1]*iv%MOD;
    }
    
    int main(){
    	N=read(),M=read(),S=read();init(N,M);
    	rin(i,0,M)W[i]=read();
    	m=(M<<1);prepare();
    	int sgn=MOD-1;
    	rin(i,0,M)sgn=MOD-sgn,A[i]=1ll*sgn*invf[i]%MOD,B[i]=1ll*W[i]*invf[i]%MOD;
    	ntt(A,1);ntt(B,1);rin(i,0,n-1)A[i]=1ll*A[i]*B[i]%MOD;ntt(A,-1);
    	int ans=0;
    	rin(i,0,M)ans=(ans+1ll*c(M,i)*c(N,i*S)%MOD*fac[i*S]%MOD*qpow(qpow(fac[S],i),MOD-2)%MOD*qpow(M-i,N-i*S)%MOD*fac[i]%MOD*A[i])%MOD;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    memcached在大负载高并发网站上的应用(转)
    NHibernate in Action(序,前言,致谢)
    php 数据类型
    w3wp 备忘录
    EF实例化Context
    爬虫程序判断是否已抓URL
    NHibenate in Action(目录)
    C#中静态构造函数的学习
    webservice 无法在网页中进行测试问题
    汉诺塔问题C#
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10646566.html
Copyright © 2011-2022 走看看