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+2n−1]和 [ b , b + 2 m − 1 ] [b,b+2^m-1] [b,b+2m−1]异或起来的答案区间,其中 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 +2n−1],每个数对应有 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
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}
ans+=Ci+ji∗Ca−c−i+d−1d−1∗Cb−d−j+c−1c−1
特判一下 c = 0 和 d = 0 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;
}