December Challenge 2019 Division 1 题解

传送门

当我打开比赛界面的时候所有题目都已经被一血了……

BINXOR

直接把异或之后二进制最多和最少能有多少个(1)算出来,在这个范围内枚举,组合数算一下就行了。注意(1)的个数是(2)(2)个变的

//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 inc(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=2e5+5;
char s[N];int cnt[2][2],fac[N],ifac[N],n,T,res,l,r;
inline void init(int n=2e5){
	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);
}
inline int C(R int n,R int m){return m<0||m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
int main(){
//	freopen("testdata.in","r",stdin);
	init();
	for(scanf("%d",&T);T;--T){
		scanf("%d",&n),cnt[0][0]=cnt[0][1]=cnt[1][0]=cnt[1][1]=0;
		scanf("%s",s+1);fp(i,1,n)++cnt[0][s[i]-'0'];
		scanf("%s",s+1);fp(i,1,n)++cnt[1][s[i]-'0'];
		l=n-min(cnt[0][0],cnt[1][0])-min(cnt[0][1],cnt[1][1]);
		r=min(cnt[0][0],cnt[1][1])+min(cnt[1][0],cnt[0][1]);
//		printf("%d %d
",l,r);
		assert((r-l)&1^1);
		res=0;
		for(R int i=l;i<=r;i+=2)upd(res,C(n,i));
		printf("%d
",res);
	}
	return 0;
}

CHFRAN

把首先原问题肯定是找某个分界线,然后左边一组右边一组最优,所以我们把所有线段转成([l,r-1]),然后可以转化为对于某个点只要它没有被覆盖且左右都有线段即合法,那么我们枚举这个点,把所有跨过它的线段全部删掉就行了

//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 N=5e5+5;
int b[N],c[N],sf[N],fs[N],l[N],r[N],n,T,tot,res;
int main(){
//	freopen("testdata.in","r",stdin);
	for(scanf("%d",&T);T;--T){
		scanf("%d",&n),res=n+1,tot=0;
		fp(i,1,n){
			scanf("%d%d",&l[i],&r[i]),--r[i];
			b[++tot]=l[i],b[++tot]=r[i],b[++tot]=l[i]-1,b[++tot]=r[i]+1;
		}
		sort(b+1,b+1+tot),tot=unique(b+1,b+1+tot)-b-1;
		fp(i,0,tot+1)c[i]=sf[i]=fs[i]=0;
		fp(i,1,n){
			l[i]=lower_bound(b+1,b+1+tot,l[i])-b;
			r[i]=lower_bound(b+1,b+1+tot,r[i])-b;
			++c[l[i]],--c[r[i]+1],++sf[r[i]],++fs[l[i]];
		}
		fp(i,1,tot+1)c[i]+=c[i-1],sf[i]+=sf[i-1];
		fd(i,tot,0)fs[i]+=fs[i+1];
		fp(i,1,tot)if(c[i]<n-1&&sf[i-1]>0&&fs[i+1]>0)cmin(res,c[i]);
		printf("%d
",res==n+1?-1:res);
	}
	return 0;
} 

BINADD

首先特判掉(B=0)的情况,然后打个表发现答案就是(A+B)二进制时最长连续进位个数(+1)(比方说A的第(0)位和(B)的第(0)位都是(1),且从(1)(k)(A)(B)恰好有一个为(1),那么(A+B)的时候这里就会连续进位(k+1)次)

发现规律之后证明就是显然的了,因为它实现的二进制加法就是每次把会进位的那些提出来单独处理,那么最多只会做最长连续进位个数次了

//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 N=1e5+5;
char s[N],t[N];int n,m,lim,res,cnt,T;
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("%s%s",s+1,t+1),n=strlen(s+1),m=strlen(t+1),lim=max(n,m);
		if(m==1&&t[m]=='0'){puts("0");continue;}
		reverse(s+1,s+1+n),reverse(t+1,t+1+m);
		fp(i,1,n)s[i]-='0';fp(i,n+1,lim)s[i]=0;
		fp(i,1,m)t[i]-='0';fp(i,m+1,lim)t[i]=0;
		res=0;
		fp(i,1,lim)if(s[i]&t[i]){
			cnt=1,++i;
			while(i<=lim&&(s[i]^t[i]))++i,++cnt;
			--i,cmax(res,cnt);
		}
		printf("%d
",res+1);
	}
	return 0;
}

STICNOT

贪心都不会,被学弟鄙视了

首先我们发现,点和边随便乱放,而且一个点的权值一定要大于等于它连接的所有边权的最大值

那么我们把边从大到小往里加入,加入的第一条边连接的两个点,点权的下界就已经确定了(以为不可能有别的边比第一条边边权更大),加入第二条边的时候,容易发现肯定是与这两个点其中的某一个相连是最优的(因为这样可以只确定一个点的下界,另一个点的下界之后才被确定,而后确定的下界一定比先确定的下界要小)。那么相当于除了第一条边需要两个点之外,其余每条边都需要一个大于等于它权值的点,直接贪心即可,树的形态都不用管

//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 N=1e5+5;
int a[N],b[N],n,res,T;
int calc(){
	for(R int i=1,j=1;i<=n;++i){
		while(j<=n&&a[j]<b[i])++j;
		if(j>n)return n-i+1;
		++j;
	}
	return 0;
}
int main(){
//	freopen("testdata.in","r",stdin);
	for(scanf("%d",&T);T;--T){
		scanf("%d",&n);
		for(R int i=1,u,v;i<n;++i)scanf("%d%d%d",&u,&v,&b[i]);
		fp(i,1,n)scanf("%d",&a[i]);
		sort(b+1,b+n),b[n]=b[n-1];
		sort(a+1,a+1+n);
		printf("%d
",calc());
	}
	return 0;
}

BINOFEV

颓柿子

[egin{aligned} S &=sum_{i=0}^n{pchoose r}\ &={1over r!}sum_{i=0}^n left(p^i ight)^{underline r} end{aligned} ]

所以如果我们能把(x^{underline r})转成普通多项式,那么对于(x^i)那一项,前面系数相等,后面对于所有的(p^j)来说是一个等比数列求和,那么就可以计算了

然而我只会(O(nlog^2 n))的分治(NTT)做法……一看数据范围就是点名被卡的……

然后翻了翻具体数学,得知

[egin{aligned} & x^{underline n}=sum_kegin{bmatrix}{n\ k}end{bmatrix}(-1)^{n-k}x^{k}\ end{aligned} ]

又翻了翻洛谷,发现第一类斯特林数的行可以(O(nlog 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;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int inc(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;
}
int kkk(R int x,R int y){
	R int res=1,d=1;
	for(;y;y>>=1,d=mul(d,x+1),x=mul(x,x))if(y&1)res=inc(mul(res,x),d);
	return res; 
}
const int N=(1<<20)+5;
int fac[N],ifac[N],lg[N],r[25][N],up[N],rt[2][N],inv[25];
int lim,d;
inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
void init(){
    fac[0]=ifac[0]=1;fp(i,1,1048576)fac[i]=mul(fac[i-1],i);
    ifac[1048576]=ksm(fac[1048576],P-2);fd(i,1048575,1)ifac[i]=mul(ifac[i+1],i+1);
    fp(d,1,20){
        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),lg[1<<d]=d;
        fp(i,1<<(d-1),(1<<d)-1)up[i]=(1<<d);
    }
    for(R int t=(P-1)>>1,i=1,x,y;i<1048576;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]]);
    R 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(rt[ty][mid+k],A[j+k+mid])),
                A[j+k]=inc(A[j+k],t);
    if(!ty){
        t=inv[d];
        fp(i,0,lim-1)A[i]=mul(A[i],t);
    }
}
int f[N],n,T,m,p,res;
void solve(int *b,int len){
	if(!len)return b[0]=1,void();
	solve(b,len>>1);
	lim=up[len],d=lg[lim];
	int dm=(len>>1);
	static int A[N],B[N];
	for(R int i=0,c=1;i<=dm;++i,c=mul(c,dm))A[i]=mul(c,ifac[i]);
	fp(i,0,dm)B[dm-i]=mul(b[i],fac[i]);
	fp(i,dm+1,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);
	reverse(A,A+dm+1);
	fp(i,0,dm)A[i]=mul(A[i],ifac[i]);fp(i,dm+1,lim-1)A[i]=0;
	fp(i,0,dm)B[i]=b[i];fp(i,dm+1,lim-1)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)b[i]=A[i];
	if(len&1){
		fd(i,len,1)b[i]=inc(mul(b[i],len-1),b[i-1]);
		b[0]=mul(b[0],len-1);
	}
}
int main(){
//	freopen("testdata.in","r",stdin);
	init();
	for(scanf("%d",&T);T;--T){
		scanf("%d%d%d",&n,&p,&m);
		fp(i,0,m)f[i]=0;
		solve(f,m);
		res=0;
		for(R int i=0,c=1;i<=m;++i,c=mul(c,p)){
			R int ret=mul(f[i],kkk(c,n));
			upd(res,(m-i)&1?P-ret:ret);
		}
		printf("%d
",mul(res,ifac[m]));
	}
    return 0;
}

APAIRS

居然被一道sb题卡了这么久……

首先,计算(score(i,j))的话,肯定是把两个数的十进制每一位从小到大排序,然后依次配对,这样一定是最优的,证明随便证

然后我们发现位与位之间的贡献是独立的,我们可以设(f_{i,j})表示从小到大第(i)位为(j)的方案数,然后每次用(f_{i,j})(f_{i,k})更新答案就行了

然后关于计算(f_{i,j}),我们可以直接枚举(j)的个数,以及小于(j)的个数,直接(dp)就行了,具体可以看代码

//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 inc(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;
}
typedef long long ll;
ll l,r;int f[25][25],as[25][25],fac[25],ifac[25],inv[25];
int nd[25],sg[25],cnt[5],K,T,res;
void init(int n=20){
	fac[0]=ifac[0]=fac[1]=ifac[1]=inv[0]=inv[1]=1;
	fp(i,2,n){
		fac[i]=mul(fac[i-1],i);
		inv[i]=mul(P-P/i,inv[P%i]);
		ifac[i]=mul(ifac[i-1],inv[i]);
	}
	fp(i,0,9)fp(j,0,9)as[i][j]=abs(i-j);
}
inline int calc(R int a,R int b,R int c){
	return 1ll*fac[a+b+c]*ifac[a]%P*ifac[b]%P*ifac[c]%P*ksm(K,a)%P*ksm(9-K,c)%P;
}
void solve(int a,int b,int c,int op){
	if(K==0&&a||K==9&&c)return;
	cnt[0]=a,cnt[1]=b,cnt[2]=c;
	R int res=0;
	fd(i,20,1){
		fp(j,0,nd[i]-1)if(cnt[sg[j]]){
			--cnt[sg[j]];
			upd(res,calc(cnt[0],cnt[1],cnt[2]));
			++cnt[sg[j]];
		}
		if(!cnt[sg[nd[i]]])break;
		--cnt[sg[nd[i]]];
		if(i==1)++res;
	}
	if(op==-1)res=inc(0,P-res);
	fp(i,a+1,a+b)upd(f[i][K],res);
}
int main(){
//	freopen("testdata.in","r",stdin);
	init();
	for(scanf("%d",&T);T;--T){
		scanf("%lld%lld",&l,&r),--l;
		res=0,memset(f,0,sizeof(f));
		fp(i,1,20)nd[i]=r%10,r/=10;
		fp(k,0,9){
			fp(i,0,k-1)sg[i]=0;fp(i,k+1,9)sg[i]=2;
			sg[k]=1,K=k;
			fp(i,0,19)fp(j,0,19-i)solve(i,20-i-j,j,1);
		}
		fp(i,1,20)nd[i]=l%10,l/=10;
		fp(k,0,9){
			fp(i,0,k-1)sg[i]=0;fp(i,k+1,9)sg[i]=2;
			sg[k]=1,K=k;
			fp(i,0,19)fp(j,0,19-i)solve(i,20-i-j,j,-1);
		}
		fp(i,1,20)fp(j,0,9)if(f[i][j])fp(k,j+1,9)if(f[i][k])
			upd(res,2ll*f[i][j]*f[i][k]%P*as[j][k]%P);
		printf("%d
",res);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/yuanquming/p/11997360.html