CodefChef September Challenge 2019 题解

传送门

\(CHEFK1\)

首先连出一个环和所有的自环,剩下的每次按\(n\)个一连就可以了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
ll n,m,res;int T;
int main(){
	for(scanf("%d",&T);T;--T){
		scanf("%lld%lld",&n,&m);
		if(m<n-1||m>(n*(n+1)>>1)){puts("-1");continue;}
		if(n==1){puts(m?"1":"0");continue;}
		if(n==2){puts(m==1?"1":"2");continue;}
		if(m<=n+n){puts(m>n+1?"3":"2");continue;}
		m-=n+n,res=3,res+=(m/n)<<1,m%=n;
		if(m)m<=(n>>1)?++res:res+=2;
		printf("%lld\n",res);
	}
	return 0;
}

\(FUZZYLIN\)

首先一个区间合法当且仅当这个区间的\(\gcd\)\(k\)的因子,用裴蜀定理就可以证明

对于一个固定的左端点,右端点在右移的过程中不同的\(\gcd\)个数只有\(O(\log n)\)个,那么我们枚举左端点,每一次二分找到下一个不同的右端点就行了,最后\(O(n\log n)\)预处理答案

总复杂度\(O(n\log^2 n)\)

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll cnt[N],s[N];int st[N][25],a[N],Log[N],n,q;
int gcd(R int x,R int y){return !y?x:gcd(y,x%y);}
inline int Gcd(R int l,R int r){
	R int k=Log[r-l+1];
	return gcd(st[l][k],st[r-(1<<k)+1][k]);
}
void find(R int pos){
	R int l,r,mid,ans,las=pos,p;
	while(las<=n){
		l=las,r=n,ans=las,p=Gcd(pos,las);
		if(p==1)return cnt[1]+=n-las+1,void();
		while(l<=r){
			mid=(l+r)>>1;
			Gcd(pos,mid)==p?(ans=mid,l=mid+1):r=mid-1;
		}
		if(p<=1e6)cnt[p]+=ans-las+1;
		las=ans+1;
	}
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d",&n);
	fp(i,1,n)scanf("%d",&a[i]),st[i][0]=a[i];
	fp(i,2,n)Log[i]=Log[i>>1]+1;
	for(R int j=1;n>>j;++j)fp(i,1,n-(1<<j)+1)
		st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	fp(i,1,n)find(i);
	for(R int i=1;i<=1e6;++i)for(R int j=i;j<=1e6;j+=i)s[j]+=cnt[i];
	scanf("%d",&q);
	for(R int i=1,x;i<=q;++i)scanf("%d",&x),printf("%lld\n",s[x]);
	return 0;
}

\(LAPD\)

首先可以\(--a,--c\)之后化成\(ax^2+2bxy+cy^2>0\),且这个条件不成立当且仅当\(x,y\)异号,那么可以化成\(ax^2-2bxy+cy^2>0\)

那么要求左边那个函数的最小值,可以对\(x\)求个偏导,则\(2ax-2by=0\),即\(y={a\over b}x\)

带入之后化简,可得\(ac>b^2\),所以这就是我们要数的数对的个数

我们枚举\(b\),那么分为三种情况,如果\(a,c>x\)肯定合法,否则\(a,c\)中只有一个\(\leq b\),那么我们枚举那个小于等于\(b\)的是多少,并计算出另外一个数的下界即可

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=2.5e7+5;
int s[N],T,a,b,c,res,p,sum,n,m,x;
inline int min(R int x,R int y){return x<y?x:y;}
inline int max(R int x,R int y){return x>y?x:y;}
int main(){
//	freopen("testdata.in","r",stdin);
	for(scanf("%d",&T);T;--T){
		scanf("%d%d%d",&a,&b,&c),res=0,--a,--c;
		m=b*b;
		fp(i,1,b){
			fp(j,1,min(i,a)){
				x=i*i/j+1;
				if(x<=c)upd(res,c-x+1);
			}
			fp(k,1,min(i,c)){
				x=i*i/k+1;
				if(x<=a)upd(res,a-x+1);
			}
			if(a>i&&c>i)upd(res,mul(a-i,c-i));
		}
		printf("%d\n",res);
	}
	return 0;
}

\(EXPGCD\)

我们枚举\(\gcd=i\),然后考虑如何计算\(n\)个数中选出\(k\)个数使其\(\gcd\)\(i\)

首先,\(对于n=[i\times x,i\times (x+1)-1]\),答案是一样的,那么我们可以算出一个之后用前缀和来处理

其次,设\(p=\left\lfloor{n\over k}\right\rfloor\),则问题可以转化为从\([1,p]\)中选择\(k\)个数使其\(\gcd\)\(1\),而\(\gcd=1\)这个条件可以用\(\mu\)容斥掉

那么我们可以先\(O(n\log n)\)预处理出\(g[i]\)表示从\([1,i]\)中选出\(k\)个数使其\(\gcd\)\(1\)的方案,然后再用\(g\)算出\(\gcd\)\(i\)时对每一个位置对应期望的贡献,最后前缀和一下即可

具体细节可以参考代码

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=5e5+5;
bitset<N>vis;int fac[N],ifac[N],g[N],ans[N],p[N],mu[N],sum[N];
int n,m,k,q;
inline int A(R int n,R int m){return mul(fac[n],ifac[n-m]);}
void init(int n){
	fac[0]=ifac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
	mu[1]=1;
	fp(i,2,n){
		if(!vis[i])p[++m]=i,mu[i]=-1;
		for(R int j=1;j<=m&&i*p[j]<=n;++j){
			vis[i*p[j]]=1;
			if(i%p[j]==0)break;
			mu[i*p[j]]=-mu[i];
		}
	}
	fp(i,1,n)upd(mu[i],P);
	fp(i,1,n/k)if(mu[i]){
		for(R int j=i*k,c=k,s;j<=n;j+=i,++c)
			s=mul(A(c,k),mu[i]),upd(g[j],s),upd(g[j+i],P-s);
	}
	fp(i,1,n)upd(g[i],g[i-1]);
//	fp(i,1,10)printf("%d %d\n",i,g[i]);
	fp(i,1,n/k){
		for(R int j=i*k,c=k,s;j<=n;j+=i,++c)
			s=mul(g[c],i),upd(sum[j],s),upd(sum[j+i],P-s);
	}
	fp(i,1,n)upd(sum[i],sum[i-1]);
//	fp(i,1,10)printf("%d %d\n",i,sum[i]);
	fp(i,k,n)sum[i]=mul(sum[i],mul(fac[i-k],ifac[i]));
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&q,&k);
	init(2e5);
	while(q--)scanf("%d",&n),printf("%d\n",sum[n]);
	return 0;
}

\(DOOFST\)

首先我们发现如果两个点之间没有边,那么它们必须一直在同一个集合中,而如果两个点有边代表它们至少要有一次在不同的集合中,所以原题有解当且仅当它的补图的每一个连通块都是完全图,而如果把补图的每一个连通块缩成一个点,对应的原图必定是一个完全图

那么我们先来考虑如果是完全图怎么处理,这个可以每一次把一个大小为\(size\)的连通块平均分成两份,然后把两个小连通块扔到两个不同的集合里,再对这两个小连通块也进行这样的操作。这样重复下去直到所有的小连通块的大小都为\(1\),容易证明这样总的操作次数是最小的

然后再回来考虑怎么判断合法以及缩点,我们可以把缩点的条件弱化一下,变成如果\((u,v)\)之间没有边就令他们在同一个连通块中,记\(S\)为还没有被缩过点的集合,每一次取出\(S\)中的开头元素\(u\),并把所有与\(u\)有边相邻的点标记,之后再扫一遍\(S\),把其中所有没被标记过的点取出来,如果没有标记就把它和\(u\)放在同一个连通块里并从\(S\)中删去这个点就是了,这样的话每个点最多被扫到\(deg_i+1\)次,因为我是用链表模拟\(set\)所以总复杂度是\(O(n+m)\)

最后缩完点之后,计算一下每个联通块应该有的边数和给出的边数加起来是否等于总的边数就可以判断是否合法了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=5e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int ans[N],tl[N],tr[N],stl[N],str[N],sz[N],tt,top,n,m;
int las[N],nxt[N],vis[N],bl[N],bg,tim;ll sum;
void solve(){
	sz[1]=0;fp(i,2,tim)sz[i]=sz[(i+1)>>1]+1;
	if(1ll*n*sz[tim]>1e6)return puts("-1"),void();
	printf("%d\n",sz[tim]);
	++top,stl[top]=1,str[top]=tim;
	while(top<tim){
		fp(id,1,top){
			R int l=stl[id],r=str[id],mid=(l+r)>>1;
			fp(i,l,mid)ans[i]=0;
			fp(i,mid+1,r)ans[i]=1;
		}
		fp(i,1,n)putchar(ans[bl[i]]+'0');
		putchar('\n');
		fp(i,1,top)tl[i]=stl[i],tr[i]=str[i];tt=top;
		top=0;
		fp(id,1,tt){
			R int l=tl[id],r=tr[id],mid=(l+r)>>1;
			++top,stl[top]=l,str[top]=mid;
			if(mid+1<=r)++top,stl[top]=mid+1,str[top]=r;
		}
	}
}
inline void del(R int x){
	las[nxt[x]]=las[x],nxt[las[x]]=nxt[x];
	if(x==bg)bg=nxt[x];
}
int main(){
	scanf("%d%d",&n,&m);
	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
	fp(i,1,n)las[i]=i-1,nxt[i]=i+1;
	las[1]=nxt[n]=0,bg=1;
	while(bg){
		R int sz=0;
		++tim;
		go(bg)vis[v]=tim;
		for(R int u=bg;u;u=nxt[u])
			if(vis[u]!=tim)bl[u]=tim,++sz,del(u);
		sum+=1ll*sz*(sz-1)>>1;
	}
	if(sum+m!=1ll*n*(n-1)>>1)return puts("-1"),0;
	solve();
	return 0;
}

\(PSUM\)

首先我们发现一件事情

\[\begin{aligned} &(a+b)^k=\sum_{i=0}^k{k\over i}a^ib^{k-i}\\ &{1\over k!}(a+b)^k=\sum_{i=0}^k{a^i\over i!}{b^{k-i}\over (k-i)!}\\ \end{aligned} \]

即如果我们已经知道了\(a^i\)\(b^i\),那么可以直接卷积得到\((a+b)^i\)

那么我们记\(f[i][j]\)表示考虑到第\(i\)个物品,总共花了\(j\)的代价,对应的价值的多项式,对于贡献直接用\(NTT\)优化就可以了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=(1<<12)+5,M=105;
int fac[N],ifac[N],inv[15],r[15][N],rt[2][N],lim,d,len;
inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
void init(int n=4096){
	fp(d,1,12){
		fp(i,1,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
		inv[d]=ksm(1<<d,P-2);
	}
	fac[0]=ifac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
	for(R int t=(P-1)>>1,i=1,x,y;i<4096;i<<=1,t>>=1){
		x=ksm(3,t),y=ksm(332748118,t),rt[0][i]=rt[1][i]=1;
		fp(k,1,i-1){
			rt[1][i+k]=mul(rt[1][i+k-1],x),
			rt[0][i+k]=mul(rt[0][i+k-1],y);
		}
	}
}
void NTT(int *A,int ty){
	fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
	int t;
	for(R int mid=1;mid<lim;mid<<=1)
		for(R int j=0;j<lim;j+=(mid<<1))
			fp(k,0,mid-1){
				A[j+k+mid]=dec(A[j+k],t=mul(A[j+k+mid],rt[ty][mid+k]));
				A[j+k]=add(A[j+k],t);
			}
	if(!ty)fp(i,0,lim-1)A[i]=mul(A[i],inv[d]);
}
void ADD(int *a,int *b,int *c){
	static int A[N],B[N];
	fp(i,0,len-1)A[i]=a[i],B[i]=b[i];
	fp(i,len,lim-1)A[i]=B[i]=0;
	NTT(A,1),NTT(B,1);
	fp(i,0,lim-1)A[i]=mul(A[i],B[i]);
	NTT(A,0);
	fp(i,0,len-1)upd(c[i],A[i]);
}
int a[M],c[M],g[M][N],f[2][M][N],vis[2][N],n,s,k,t,res;
int main(){
//	freopen("testdata.in","r",stdin);
	init();
	scanf("%d%d%d",&n,&s,&k);
	fp(i,1,n)scanf("%d%d",&c[i],&a[i]);
	lim=1;while(lim<=(k<<1))lim<<=1,++d;
	len=lim>>1;
	fp(i,1,n){
		R int x=1;
		fp(j,0,k)g[i][j]=mul(x,ifac[j]),x=mul(x,a[i]);
	}
	f[0][0][0]=1,vis[0][0]=1,t=0;
	fp(i,1,n){
		memset(f[t^1],0,sizeof(f[t^1]));
		memset(vis[t^1],0,sizeof(vis[t^1]));
		fp(j,0,s)if(vis[t][j]){
			vis[t^1][j]=1;
			fp(k,0,len-1)upd(f[t^1][j][k],f[t][j][k]);
			if(j+c[i]<=s){
				vis[t^1][j+c[i]]=1;
				ADD(f[t][j],g[i],f[t^1][j+c[i]]);
			}
		}
		t^=1;
	}
	fp(i,0,s)upd(res,f[t][i][k]);
	res=mul(res,fac[k]);
	printf("%d\n",res);
	return 0;
}

\(DODGERS\)

首先把每个人所有比它\(tim\)小的点中,比它小且最大,比它大且最小分别找出来,记为\(ls_i\)\(rs_i\),那么它会对一个询问\(l,r\)有贡献当且仅当\(l\in [ls_i,i],r\in [i,rs_i]\)

这样的话就是一个二维数点问题了,主席树就可以了,复杂度\(O(n\log n)\)

代码中为了减小常数写了个自己\(yy\)的可持久化树状数组不过似乎复杂度是\(O(n\log ^2n)\)的根本跑不过主席树……

//minamoto
#include<bits/stdc++.h>
#define R register
#define pb emplace_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
typedef pair<int,int> pi;
const int N=1e6+5,M=3e7+5;
struct EG{int u,v;}E[N];
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
int c[M],id[N],dfn[N],ls[N],rs[N],st[N],vis[N],rd[N],top,tim,cnt;
vector<int>ID[N];vector<pi>sl[N];vector<int>::iterator it;
int n,m,q,T,lasans,s;
void clr(){
	fp(i,1,n+1){
		head[i]=dfn[i]=id[i]=0,
		ID[i].clear(),sl[i].clear();
//		vector<int>().swap(ID[i]),
//		vector<pi>().swap(sl[i]);
	}
	tot=cnt=tim=lasans=0;
}
void dfs(int u){dfn[u]=++tim;go(u)if(!dfn[v])dfs(v);}
int main(){
//	freopen("testdata.in","r",stdin);
	for(T=read();T;--T){
		n=read(),m=read(),q=read(),s=read();
		fp(i,1,m)E[i].u=read(),E[i].v=read();
		fd(i,m,1)add(E[i].u,E[i].v),add(E[i].v,E[i].u);
		fp(i,1,n)if(!dfn[i])dfs(i);
		fp(i,1,n)ls[i]=1,rs[i]=n;
		fp(u,1,n)go(u)if(dfn[v]<dfn[u])v<=u?cmax(ls[u],v+1):cmin(rs[u],v-1);
		fp(i,1,n){
			sl[i].pb(pi(ls[i],1)),sl[i].pb(pi(i+1,-1));
			sl[rs[i]+1].pb(ls[i],-1),sl[rs[i]+1].pb(i+1,1);
		}
		fp(i,1,n)ID[i].pb(0);
		fp(i,1,n){
			top=0;
			for(auto v:sl[i])for(R int x=v.fi;x<=n;x+=x&-x){
				if(!vis[x]){
					st[++top]=x,vis[x]=1,c[++cnt]=c[id[x]],id[x]=cnt;
					ID[x].pb(cnt);
				}
				c[id[x]]+=v.se;
			}
			fp(i,1,top)vis[st[i]]=0;
			rd[i]=cnt;
		}
		for(R int l,r;q;--q){
			l=read(),r=read(),l=(l+lasans*s-1)%n+1,r=(r+lasans*s-1)%n+1;
			if(l>r)swap(l,r);
			lasans=0;
			for(R int x=l;x;x-=x&-x){
				it=--lower_bound(ID[x].begin(),ID[x].end(),rd[r]+1);
				lasans+=c[*it];
			}
			print(lasans);
		}
		clr();
	}
	return Ot(),0;
}
posted @ 2019-09-16 21:14  源曲明  阅读(293)  评论(0编辑  收藏  举报