• 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;
    }
    
  • 相关阅读:
    对Android开发者有益的40条优化建议
    git推送tag到远端服务器
    详细注释!二维码条码扫描源码,使用Zxing core2.3
    探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法
    android的logcat详细用法!
    【Android】开源项目汇总-备用
    android 中theme.xml与style.xml的区别
    Android实现对HOME键的捕获和屏蔽
    java中newInstance()和new()
    java Stack
  • 原文地址:https://www.cnblogs.com/cjjsb/p/12044486.html
Copyright © 2020-2023  润新知