2018.10.14牛客noip提高组模拟赛(第五场)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/83050271

A-同余方程

题目 x x [ l 1 , r 1 ] [l1,r1] y y [ l 2 , r 2 ] [l2,r2] 中的正整数,求方程 x     y )   0 ( m o d m ) (x\ \bigoplus\ y)\ \equiv0\pmod{m} ( ) (\bigoplus表示异或) 的解数

结果 m o d   998244353 mod\ 998244353

solution:

首先可以将区间转化成前缀和,这样我们只要考虑 [ 0 , a ] [0,a] [ 0 , b ] [0,b] 的答案,考虑一个区间异或另一个区间最后的答案区间到底是什么,有了这个只要除以 m m 就是答案了 . .

如果一个区间 [ a , a + 2 n 1 ] [a,a+2^n-1] [ b , b + 2 m 1 ] [b,b+2^m-1] 异或起来的答案区间,其中 a a 的后 n n 位和 b b 的后 m m 位为 0 0 ,假设 n > m n>m ,那么答案区间就是 [ a     b , a     b   + 2 n 1 ] [a\ \bigoplus\ b,a\ \bigoplus\ b\ +2^n-1] ,每个数对应有 2 m 2^m 种,因为每个 b b 都有对应的 a a 来异或到区间内的某个数,所以枚举每一位 1 1 ,算出答案区间更新答案

注意因为 l 1 , l 2 l1,l2 0 0 的情况,所以可以都看成开区间

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
LL l1,l2,r1,r2,m;
const int mod=998244353;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline LL solve(LL a,LL b){
	LL ans=0,mx,mn,ql,qr;
	for(int i=0;i<=62;i++)
		if((a>>i)&1)
			for(int j=0;j<=62;j++)
				if((b>>j)&1){
					LL x1=(1LL<<i),x2=(1LL<<j);
					mx=max(x1-1,x2-1),mn=min(x1,x2);
					ql=((a^x1)^(b^x2))&(~mx);
					qr=ql|mx;
					LL now=(qr/m-ql/m)%mod;
					if(ql%m==0) (++now)%=mod;
					(ans+=mn%mod*now%mod)%=mod;
				}
	return ans;
}

int main(){
	scanf("%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&m);
	LL ans=solve(r1+1,r2+1)-solve(r1+1,l2)-solve(l1,r2+1)+solve(l1,l2);
	ans=(ans%mod+mod)%mod;
	printf("%lld\n",ans);
	return 0;
}

B-旅游

题目: 链接:https://www.nowcoder.com/acm/contest/177/B

在可怜的计划中,可怜一共打算游玩 n 个景点,这些景点被 m 条双向道路联通(即任何两个景点之间都能通过道路直接或者间接到达)。第 i 条道路的长度为 2i。

因为这 n 个景点中,只有 1 号景点在机场附近,所以可怜想要制定一个从 1 号点出发,沿着道路一路游玩,并在最后回到 1 号点的游览计划。同时因为每一条道路都有不一样的风景,于是可怜想要在这个计划中,经过每一条道路至少一次(只要从一个方向走过就算经过过这条道路)。

令一个游览计划的疲劳度为行走长度的总和(多次经过的边长度被多次计算),可怜想要计算所有满足条件的游览计划中疲劳度的最小值。

solution:

因为这个边权十分特殊,有一个结论就是,求出一个最小生成树,所有非树边一定只用经过一次(因为树边和一定小于这个非树边),然后每个非树边可以看成覆盖了 u , v u,v 的一条路径,每个点需要被覆盖偶数次才能保证回到 1 1 号节点,所以只要看这个点被覆盖次数是奇数或者偶数就好了 . . 如果是奇数这个树边就要走一次偶数就要走两次

l o n g   l o n g \color{red}以后要是再手懒乘爆long\ long 就锤死自己!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 500005
#define LL long long
using namespace std;
int n,m,fa[N],cnt=1,head[N],mark[N];
bool used[N<<1];
LL ans,val[N];
const int mod=998244353;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

struct EDGE{
	int to,nxt; LL w;
}edge[N<<1];
inline void add(int x,int y,LL z){
	edge[++cnt].to=y; edge[cnt].w=z; edge[cnt].nxt=head[x]; head[x]=cnt;
}

inline int find(int x){
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}

inline void dfs(int u,int fr){
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(!used[i] || v==fr) continue;
		dfs(v,u);
		if(mark[v]) (ans+=edge[i].w)%=mod;
		else (ans+=edge[i].w*2%mod)%=mod;
		mark[u]^=mark[v];
	} return;
}

int main(){
	n=rd(); m=rd(); val[0]=1;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){
		int x=rd(),y=rd(); val[i]=(val[i-1]<<1)%mod;
		add(x,y,val[i]); add(y,x,val[i]);
		int u=find(x),v=find(y);
		if(u==v) continue;
		fa[u]=v; used[cnt]=used[cnt^1]=true;
	}
	for(int i=2;i<=cnt;i+=2)
		if(!used[i]){
			(ans+=edge[i].w)%=mod;
			mark[edge[i].to]^=1;
			mark[edge[i^1].to]^=1;
		}
	dfs(1,0);
	printf("%lld\n",ans);
	return 0;
}

C-串串

题目: 链接:https://www.nowcoder.com/acm/contest/177/C

给定非负整数a,b,c,d,求有多少对01串(S,T),满足以下条件:

- S 由 a 个 0 , b 个 1 组成
- T 由 c 个 0 , d 个 1 组成
- T 可以由 S 删掉一些字符得到

由于答案可能过大,你只需要输出答案对 1000000007 取模后的值

solution:

一眼看上去就感觉是组合数,奈何功力不够最后没有调出来

很容易想到对于不同的 T T 都是一样的,一共有 C c + d d C_{c+d}^d T T ,对每个 T T 都可以添加一些字符变成一个 S S ,如何保证不算重呢,我们可以限制 T T 必须是 S S 中同样的子序列中字典序最小的那个,可以发现如果在 T T 中插入 0 0 ,只能把它插入 1 1 的前面,因为插入 0 0 前面就不是字典序最小的了, 1 1 的情况同理,这就可以看成隔板法,如何组合数求一下就好了,但是注意如果放在 T T 的后面就可以随便放了,所以枚举在后面放 i 0 j 1 i个0,j个1 ,那么
a n s + = C i + j i C a c i + d 1 d 1 C b d j + c 1 c 1 ans+=C_{i+j}^i*C_{a-c-i+d-1}^{d-1}*C_{b-d-j+c-1}^{c-1}

特判一下 c = 0 d = 0 c=0和d=0 的情况就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 4005
#define LL long long
using namespace std;
int a,b,c,d,n,m;
LL ans,fac[maxn],inv[maxn];
const int mod=1e9+7;

inline LL qpow(LL x,int k){
	LL ret=1;
	while(k){
		if(k&1) (ret*=x)%=mod;
		(x*=x)%=mod; k>>=1;
	} return ret;
}

inline LL C(int x,int y){
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}

int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out2.txt","w",stdout);
	scanf("%d%d%d%d",&a,&b,&c,&d);
	n=a+b,m=c+d; fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	inv[n]=qpow(fac[n],mod-2);
	for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
	if(c==0){
		for(int i=0;i<=a-c;i++)
			for(int j=b-d;j<=b-d;j++)
				(ans+=C(i+j,i)*C(a-c-i+d-1,d-1)%mod)%=mod;
	}
	else if(d==0){
		for(int i=a-c;i<=a-c;i++)
			for(int j=0;j<=b-d;j++)
				(ans+=C(i+j,i)*C(b-d-j+c-1,c-1)%mod)%=mod;
	}
	else{
		for(int i=0;i<=a-c;i++)
			for(int j=0;j<=b-d;j++)
				(ans+=C(i+j,i)*C(a-c-i+d-1,d-1)%mod*C(b-d-j+c-1,c-1)%mod)%=mod; 
	}
	printf("%lld\n",C(m,d)*ans%mod);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/83050271