Codechef December Challenge 2019 Division 1

Preface

断断续续打完了,难得的总分上了700

最后苟到了Rank16,下一场如果还能保持这样的话应该可以上7星了QAQ


Binary XOR

显然我们只需要求出最后异或完了之后(0)(1)的个数即可

那么我们先求出(0)个数的上下界,然后枚举一下具体的个数,注意这个个数一定是两个两个变化的

方案数的话直接组合数算一下即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,mod=1e9+7;
int t,n,a0,a1,b0,b1,ans,L,R,fact[N],inv[N]; char s[N];
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
	return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	for (scanf("%d",&t),init(100000);t;--t)
	{
		RI i; scanf("%d",&n); ans=a0=a1=b0=b1=0;
		for (scanf("%s",s+1),i=1;i<=n;++i) if (s[i]=='0') ++a0; else ++a1;
		for (scanf("%s",s+1),i=1;i<=n;++i) if (s[i]=='0') ++b0; else ++b1;
		for (L=n-min(a0,b0)-min(a1,b1),R=min(a0,b1)+min(a1,b0),i=L;i<=R;i+=2)
		inc(ans,C(n,i)); printf("%d
",ans);
	}
	return 0;
}

Addition

首先研究下那段伪代码,很容易发现它的原理:先加起来(用(operatorname{xor}))然后把要进位的(用(operatorname{and}))留到下一次去加

那么答案就容易得出了,要连续操作就意味着要进行连续的进位,相当于是找出一段最长的以两个(1)开始的接下来全是(0,1)的长度

先注意对齐之后直接扫一遍即可

#include<cstdio>
#include<iostream>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,la,lb,ans; char a[N],b[N];
inline void expand(char *s,int& len,CI tar)
{
	int dlt=tar-len; RI i; for (i=tar;i-dlt>0;--i) s[i]=s[i-dlt];
	for (i=1;i<=dlt;++i) s[i]='0'; len=tar;
}
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i,j; scanf("%s%s",a+1,b+1); la=strlen(a+1); lb=strlen(b+1);
		if (lb==1&&b[1]=='0') { puts("0"); continue; }
		int tp=max(la,lb)+1; expand(a,la,tp); expand(b,lb,tp);
		for (ans=0,i=la;i;i=j)
		{
			if (a[i]!='1'||b[i]!='1') { j=i-1; continue; }
			for (j=i-1;j&&a[j]!=b[j];--j); ans=max(ans,i-j);
		}
		printf("%d
",ans+1);
	}
	return 0;
}

Chefina and Ranges

本来搞了个很复杂的做法然后被陈指导嘲讽了QAQ

首先把区间变成左闭右开,然后离散化之后大力枚举分界点的位置(i),要满足(i)左边所有区间右端点(le i),右边所有区间左端点(>i),并且两边都要有至少一个区间

那么这个时候我们容易发现,只要枚举(i),范围是所有区间中最小的右端点(含)到最大的左端点(不含)

然后就是求跨过这个点的区间个数了,直接差分+前缀和即可

#include<cstdio>
#include<algorithm>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,INF=1e9;
int t,n,ans,l[N],r[N],L,R,rst[N<<1],num,dlt[N<<1];
inline int find(CI x)
{
	return lower_bound(rst+1,rst+num+1,x)-rst;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),num=R=0,ans=L=INF,i=1;i<=n;++i)
		{
			scanf("%d%d",&l[i],&r[i]);
			rst[++num]=l[i]; rst[++num]=r[i]; L=min(L,r[i]); R=max(R,l[i]);
		}
		if (L>R-1) { puts("-1"); continue; }
		sort(rst+1,rst+num+1); num=unique(rst+1,rst+num+1)-rst-1;
		for (i=1;i<=n;++i) ++dlt[find(l[i])],--dlt[find(r[i])];
		for (i=1;i<=num;++i) dlt[i]+=dlt[i-1]; int lim=find(R);
		for (i=find(L);i<lim;++i) ans=min(ans,dlt[i]);
		for (printf("%d
",ans!=INF?ans:-1),i=1;i<=num;++i) dlt[i]=0;
	}
	return 0;
}

STICNOT

首先容易想到二分+贪心,每次变点权显然是把最小的(mid)个改成(infty)

然后考虑怎么check,下面提供一种方法:

(a,b)从小到大排序,当且仅当任意(iin[1,n))都有(a_ige b_i),此时才存在一种构造方式符合题目要求

充分性画画图就证出来了,必要性留作自己思考

最后连树的形态都不用管……

#include<cstdio>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,x,y,a[N],b[N],ans;
inline bool check(CI st)
{
	RI i; for (i=1;i<n&&st+i<=n;++i)
	if (a[st+i]<b[i]) return 0; return 1;
}
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<n;++i)
		scanf("%d%d%d",&x,&y,&b[i]);
		for (i=1;i<=n;++i) scanf("%d",&a[i]);
		sort(a+1,a+n+1); sort(b+1,b+n);
		int l=0,r=n,mid; while (l<=r)
		if (check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;
		printf("%d
",ans);
	}
	return 0;
}

Test Generation

瞎JB乱搞题,大力无脑随机喜提73pts

PS:题目中说了不能有重边但checker没有判233

#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
const int N=76,M=125,S=35;
using namespace std;
typedef pair <int,int> pi;
int t; vector <pi> E;
inline int Rand(CI x)
{
	return 1LL*rand()*rand()%x+1;
}
inline void link(CI x,CI y)
{
	E.push_back(make_pair(x,y));
}
inline void Output(void)
{
	random_shuffle(E.begin(),E.end());
	for (vector <pi>:: iterator it=E.begin();it!=E.end();++it)
	printf("%d %d
",it->first,it->second); E.clear();
}
int main()
{
	srand(20030909); RI i; printf("%d %d
",N+4,M+8);
	for (i=2;i<=N;++i) link(Rand(i-1),i);
	for (i=1;i<=S;++i) link(1,Rand(N));
	for (i=N;i<=M-S;++i) link(Rand(N),Rand(N));
	for (i=1;i<=4;i+=2) link(i+N,N),link(i+N,i+N+1),link(i+N,N),link(i+N,i+N+1);
	for (Output(),i=1;i<=N-1;++i) link(i,N-Rand(N-i)+1);
	for (i=1;i<=S;++i) link(N,Rand(N));
	for (i=N;i<=M-S;++i) link(Rand(N),Rand(N));
	for (i=1;i<=4;i+=2) link(i+N,N),link(i+N,N),link(i+N,i+N+1),link(i+N,i+N+1);
	return Output(),0;
}

Scoring Pairs

首先容易想到分开来算贡献,考虑两个数计算贡献一定是排序后按位来减一减,因此如果我们能求出一个数组(w_{i,j})表示从小到大排序后第(i)位的数字(j)出现的次数,就可以直接算答案

考虑求这个个数,是个人都知道用数位DP

考虑我们先枚举一位(p),假定这一位没卡到上界,即后面的位都可以随便选

然后枚举这位的数字(s),再枚举一个(i)表示算数字(i)的贡献

计算过程比较巧妙,枚举一个(j)表示(i)这个数的个数,然后我们发现只要再枚举一个(k)表示小于(i)的数个数那么数字(i)所在的大小区间就被定下来了,而此时显然方案数也很好计算

然后就做完了,复杂度看起来很大,但众所周知常数小上界跑不到,因此跑的飞快

#include<cstdio>
#include<algorithm>
#define RI int
#define CI const int&
using namespace std;
typedef long long LL;
const int mod=1e9+7,N=20;
int t,num[N],ct[N],len,C[N][N],pw[N][N],w[N][N],ans; LL l,r;
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
	if ((x-=y)<0) x+=mod;
}
inline int sum(CI x,CI y)
{
	int t=x+y; return t>=mod?t-mod:t;
}
inline void init(CI n)
{
	RI i,j; for (C[0][0]=i=1;i<=n;++i) for (C[i][0]=j=1;j<=i;++j)
	C[i][j]=sum(C[i-1][j],C[i-1][j-1]); pw[0][0]=1;
	for (i=1;i<=n;++i) for (pw[i][0]=j=1;j<=n;++j) pw[i][j]=1LL*pw[i][j-1]*i%mod;
}
inline void resolve(LL x)
{
	for (RI i=0;i<20;++i) num[i]=0; len=0; while (num[++len]=x%10,x/=10);
}
inline void calc(const LL& x,CI opt)
{
	RI p,i,j,k,s,r; for (i=0;i<10;++i) ct[i]=0;
	if (!~opt) { int tp=len; resolve(x); len=tp; } else resolve(x);
	for (p=len;p;--p) //the unlimited position
	{
		for (s=0;s<num[p]+(p==1);++s) //which is put to p
		{
			for (i=s;i<10;++i) ++ct[i];
			for (i=0;i<10;++i) //calc i's contribution
			for (j=0;j<p;++j) //how many is i
			for (k=0;k<p-j;++k) //how many more is less than i
			{
				int dlt=1LL*C[p-1][j]*C[p-1-j][k]%mod*pw[i][k]%mod*pw[9-i][p-1-j-k]%mod;
				for (r=(i?ct[i-1]:0)+k+1;r<=ct[i]+k+j;++r)
				if (~opt) inc(w[r][i],dlt); else dec(w[r][i],dlt);
			}
			for (i=s;i<10;++i) --ct[i];
		}
		for (i=num[p];i<10;++i) ++ct[i];
	}
}
int main()
{
	for (scanf("%d",&t),init(19);t;--t)
	{
		scanf("%lld%lld",&l,&r); calc(r,1); calc(l-1,-1);
		RI i,j,k; for (i=1;i<=19;++i) for (j=0;j<10;++j) w[i][j]%=mod;
		for (ans=0,i=1;i<=19;++i) for (j=0;j<10;++j) for (k=0;k<10;++k)
		inc(ans,1LL*abs(j-k)*w[i][j]%mod*w[i][k]%mod); 
		for (printf("%d
",ans),i=1;i<=19;++i) for (j=0;j<10;++j) w[i][j]=0;
	}
	return 0;
}

Binomial Fever

压轴题巨TM简单。考虑:

[S=sum_{i=0}^N C_{p_i}^r=frac{1}{r} imes sum_{i=0}^N frac{(p^i)!}{(p^i-r)!}=frac{1}{r} imes sum_{i=0}^N (p^i)^{underline{r}} ]

如果我们可以把((p^i)^{underline{r}})展开得到每一项的系数,那么显然我们可以直接对同幂次的进行等比数列求和

翻一翻组合数学就知道:

[x^underline{n}=sum_{k} left [ ^n_k ight](-1)^{n-k}x^k ]

然后第一类斯特林数的(O(nlog n))做法看这里:Luogu P5408 【模板】第一类斯特林数·行

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5,mod=998244353;
int t,n,p,r,lim,ans,F[N<<1],fact[N],inv[N];
inline int sum(CI x,CI y)
{
	int t=x+y; return t>=mod?t-mod:t;
}
inline int sub(CI x,CI y)
{
	int t=x-y; return t<0?t+mod:t;
}
inline void inc(int& x,CI y)
{
	if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
}
namespace Poly
{
	int rev[N<<1],p;
	inline void init(CI n)
	{
		for (lim=1,p=0;lim<=n;lim<<=1,++p);
		for (RI i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<p-1);
	}
	inline void NTT(int *f,CI opt)
	{
		RI i,j,k; for (i=0;i<lim;++i) if (i<rev[i]) swap(f[i],f[rev[i]]);
		for (i=1;i<lim;i<<=1)
		{
			int m=i<<1,D=quick_pow(3,~opt?(mod-1)/m:mod-1-(mod-1)/m);
			for (j=0;j<lim;j+=m)
			{
				int W=1; for (k=0;k<i;++k,W=1LL*W*D%mod)
				{
					int x=f[j+k],y=1LL*f[i+j+k]*W%mod;
					f[j+k]=sum(x,y); f[i+j+k]=sub(x,y);
				}
			}
		}
		if (!~opt)
		{
			int Inv=quick_pow(lim); for (i=0;i<lim;++i) f[i]=1LL*f[i]*Inv%mod;
		}
	}
	inline void convolution(int *f,CI n,CI c,int *g)
	{
		static int A[N<<1],B[N<<1]; RI i; int bs; init(n<<1);
		for (i=0;i<n;++i) A[n-1-i]=1LL*f[i]*fact[i]%mod;
		for (bs=1,i=0;i<n;++i,bs=1LL*bs*c%mod) B[i]=1LL*bs*inv[i]%mod;
		for (i=n;i<lim;++i) A[i]=B[i]=0; NTT(A,1); NTT(B,1);
		for (i=0;i<lim;++i) A[i]=1LL*A[i]*B[i]%mod;	NTT(A,-1);
		for (i=0;i<n;++i) g[i]=1LL*A[n-1-i]*inv[i]%mod;
	}
	inline void solve(CI n,int *f)
	{
		if (!n) return (void)(f[0]=1); static int A[N<<1],B[N<<1];
		RI i; int m=n>>1; solve(m,f); convolution(f,m+1,m,A);
		for (i=0;i<=m;++i) B[i]=f[i]; for (i=m+1;i<lim;++i) A[i]=B[i]=0;
		for (init(n),NTT(A,1),NTT(B,1),i=0;i<lim;++i) A[i]=1LL*A[i]*B[i]%mod;
		NTT(A,-1); if (!(n&1)) for (i=0;i<=n;++i) f[i]=A[i]; else
		for (i=0;i<=n;++i) f[i]=sum(i?A[i-1]:0,1LL*(n-1)*A[i]%mod);
		//for (printf("%d
",n),i=0;i<=n;++i) printf("%d%c",f[i]," 
"[i==n]);
	}
};
inline int calc(CI q,CI n)
{
	if (q==1) return sum(1,n);
	return 1LL*sub(1,quick_pow(q,n+1))*quick_pow(sub(1,q))%mod;
}
int main()
{
	for (scanf("%d",&t),init(1e6);t;--t)
	{
		RI i; for (scanf("%d%d%d",&n,&p,&r),Poly::solve(r,F),ans=i=0;i<=r;++i)
		inc(ans,1LL*((r-i)&1?mod-F[i]:F[i])*calc(quick_pow(p,i),n)%mod);
		for (printf("%d
",1LL*ans*inv[r]%mod),i=0;i<=r;++i) F[i]=0;
	}
	return 0;
}

(Challenge) Cubical Virus

这场刷排名最关键的一题,随手贪心搞了35pts

考虑单独看某个(1 imes n imes n)的正方形,一共有(n)个,显然我们贪心地把空格子多的放在前面

然后排序的时候需要两个参数(玄学),第一关键字是感染后的空格子数目,第二关键字是感染前的空格子数目

三个维度排三次即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define RI register int
#define CI const int&
#define Ms(A,B) memcpy(A,B,sizeof(A))
using namespace std;
const int N=105;
struct data
{
	int val,rtg,id;
	friend inline bool operator <(const data& A,const data& B)
	{
		return A.val!=B.val?A.val<B.val:A.rtg<B.rtg;
	}
}v[N]; int n,p[N],q[N],r[N]; bool s[N][N][N],t[N][N][N],h[N][N][N];
inline char get_digit(void)
{
	char ch; while (!isdigit(ch=getchar())); return ch;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j,k; for (scanf("%d",&n),k=1;k<=n;++k)
	for (j=1;j<=n;++j) for (i=1;i<=n;++i) s[i][j][k]=get_digit()-48;
	for (Ms(h,s),i=1;i<=n;++i) for (j=1;j<=n;++j)
	for (k=1;k<=n;++k) if (h[i][j][k]) h[i][j+1][k]=h[i][j][k+1]=1;
	for (i=1;i<=n;++i) v[i].val=v[i].rtg=0;
	for (i=1;i<=n;++i) for (v[i].id=i,j=1;j<=n;++j)
	for (k=1;k<=n;++k) v[i].val+=h[i][j][k];
	for (i=1;i<=n;++i) for (v[i].id=i,j=1;j<=n;++j)
	for (k=1;k<=n;++k) v[i].rtg+=s[i][j][k];
	for (sort(v+1,v+n+1),i=1;i<=n;++i) p[i]=v[i].id;
	for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
	t[i][j][k]=s[p[i]][j][k]; Ms(s,t);
	for (Ms(h,s),j=1;j<=n;++j) for (i=1;i<=n;++i)
	for (k=1;k<=n;++k) if (h[i][j][k]) h[i+1][j][k]=h[i][j][k+1]=1;
	for (j=1;j<=n;++j) v[j].val=v[j].rtg=0;
	for (j=1;j<=n;++j) for (v[j].id=j,i=1;i<=n;++i)
	for (k=1;k<=n;++k) v[j].val+=h[i][j][k];
	for (j=1;j<=n;++j) for (v[j].id=j,i=1;i<=n;++i)
	for (k=1;k<=n;++k) v[j].rtg+=s[i][j][k];
	for (sort(v+1,v+n+1),j=1;j<=n;++j) q[j]=v[j].id;
	for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
	t[i][j][k]=s[i][q[j]][k]; Ms(s,t);
	for (Ms(h,s),k=1;k<=n;++k) for (i=1;i<=n;++i)
	for (j=1;j<=n;++j) if (h[i][j][k]) h[i+1][j][k]=h[i][j+1][k]=1;
	for (k=1;k<=n;++k) v[k].val=v[k].rtg=0;
	for (k=1;k<=n;++k) for (v[k].id=k,i=1;i<=n;++i)
	for (j=1;j<=n;++j) v[k].val+=h[i][j][k];
	for (k=1;k<=n;++k) for (v[k].id=k,i=1;i<=n;++i)
	for (j=1;j<=n;++j) v[k].rtg+=s[i][j][k];
	for (sort(v+1,v+n+1),k=1;k<=n;++k) r[k]=v[k].id;
	for (i=1;i<=n;++i) for (j=1;j<=n;++j) for (k=1;k<=n;++k)
	t[i][j][k]=s[i][j][r[k]]; Ms(s,t);
	for (i=1;i<=n;++i) printf("%d%c",p[i]," 
"[i==n]);
	for (j=1;j<=n;++j) printf("%d%c",q[j]," 
"[j==n]);
	for (k=1;k<=n;++k) printf("%d%c",r[k]," 
"[k==n]);
	return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/12044486.html