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

A-同余方程

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

结果 m o d   998244353 mod\ 998244353 mod 998244353

solution:

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

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

注意因为 l 1 , l 2 l1,l2 l1,l2 0 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 u,v的一条路径,每个点需要被覆盖偶数次才能保证回到 1 1 1号节点,所以只要看这个点被覆盖次数是奇数或者偶数就好了 . . .如果是奇数这个树边就要走一次偶数就要走两次

以 后 要 是 再 手 懒 乘 爆 l o n g   l o n g 就 锤 死 自 己 ! \color{red}以后要是再手懒乘爆long\ long 就锤死自己! 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 T都是一样的,一共有 C c + d d C_{c+d}^d Cc+dd T T T,对每个 T T T都可以添加一些字符变成一个 S S S,如何保证不算重呢,我们可以限制 T T T必须是 S S S中同样的子序列中字典序最小的那个,可以发现如果在 T T T中插入 0 0 0,只能把它插入 1 1 1的前面,因为插入 0 0 0前面就不是字典序最小的了, 1 1 1的情况同理,这就可以看成隔板法,如何组合数求一下就好了,但是注意如果放在 T T T的后面就可以随便放了,所以枚举在后面放 i 个 0 , j 个 1 i个0,j个1 i0j1,那么
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} ans+=Ci+jiCaci+d1d1Cbdj+c1c1

特判一下 c = 0 和 d = 0 c=0和d=0 c=0d=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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值