给我们两颗森林 a、b,都有
n
(
n
<
=
1
e
5
)
n~(n<=1e5)
n(n<=1e5) 个点,
我们可以进行一种操作:选择两个点
i
,
j
i,j
i,j 在 i、j 之间建立一条边,要求在操作之后,a、b 中都不能存在环
问最多可以操作多少次。
思路
对于 a、b 中的某个点
i
(
i
!
=
1
)
i~~(i!=1)
i(i!=1), 如果 i 不与 a 中的 1 连通,也不与 b 中的 1 连通,那么我们就将建立一条边 1~i,如下图 i ==3 时
把上面的情况去除掉之后,还有两种情况可以连接边(设这个边的两端点为
i
、
j
i、j
i、j):
在 a 森林中 1 与 i 有边,1 与 j 无边,在 b 森林中 1 与 i 无边,1 与 j 有边。此时我们可以连接 i 与 j。
在 a 森林中 1 与 i 无边,1 与 j 有边,在 b 森林中 1 与 i 有边,1 与 j 无边。此时我们可以连接 i 与 j。 如果下图所示(当 i2,j4 时候)
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definePirpair<int,int>#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/constint N =1e5+10;int a[N], b[N];
vector<Pir> ans;
vector<int> v1, v2;int n, m1, m2;voidinit(int n){for(int i =1; i <= n; i ++) a[i]= b[i]= i;}intfinda(int x){return a[x]== x ? x : a[x]=finda(a[x]);}intfindb(int x){return b[x]== x ? x : b[x]=findb(b[x]);}intmain(){scanf("%d %d %d",&n,&m1,&m2);init(n);int u, v;for(int i =1; i <= m1; i ++){scanf("%d %d",&u,&v);
a[finda(u)]=finda(v);}for(int i =1; i <= m2; i ++){scanf("%d %d",&u,&v);
b[findb(u)]=findb(v);}
u =finda(1), v =findb(1);for(int i =2; i <= n; i ++){if(finda(i)!= u &&findb(i)!= v){
a[finda(i)]= u;
b[findb(i)]= v;
ans.pb(m_p(1, i));}if(finda(i)!= u) v1.pb(i);if(findb(i)!= v) v2.pb(i);}for(int i =0, j =0; i < v1.size()&& j < v2.size();){
u =finda(1), v =findb(1);int x = v1[i], y = v2[j];if(finda(x)== u &&findb(x)== v){
i ++;continue;}if(finda(y)== u &&findb(y)== v){
j ++;continue;}
a[finda(x)]=finda(y);
b[findb(x)]=findb(y);
ans.pb(m_p(x, y));
i ++, j ++;}printf("%lu\n", ans.size());for(auto x : ans){printf("%d %d\n", x.fi, x.se);}return0;}
给我我们那个区间:对于第 i (1<=i<=n) 个为
[
l
i
,
r
i
]
[l_i, r_i]
[li,ri].
在 n 个区间中都选择一个数
a
i
a_i
ai,
∑
i
=
1
n
a
[
i
]
<
=
m
~~~\sum_{i=1}^{n}~a[i]~~~<=m
∑i=1na[i]<=m
且
g
c
d
(
a
1
,
a
2
,
.
.
.
,
a
n
)
=
1
gcd(a_1,a_2,...,a_n)=1
gcd(a1,a2,...,an)=1,
n
<
=
50
,
m
<
=
1
e
5
n<=50,m<=1e5
n<=50,m<=1e5
问可以选择出的
a
i
a_i
ai 数组的方案数。
思路
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definePirpair<int,int>#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/constint N =55;constint M =1e5+10;constint mod =998244353;int n, m;int l[N], r[N];int prim[M], mu[M], vis[M], cnt;voidget_mu(int n){
mu[1]=1;for(int i =2; i <= n; i ++){if(! vis[i]){
prim[cnt ++]= i;
mu[i]=-1;}for(int j =0; j < cnt && i * prim[j]<= n; j ++){
vis[i * prim[j]]=1;if(i % prim[j]==0)break;else mu[i * prim[j]]=-mu[i];}}}int dp[M], sum[M];//求 n 个在相应区间的数字的和 <= m 的方案数intf(int d){int mx = m / d;for(int i =0; i <= mx; i ++) dp[i]=0;//注意不要每次的 memset 初始化 dp
dp[0]=1;for(int i =1; i <= n; i ++){int L =(l[i]+ d -1)/ d, R = r[i]/ d;if(L > R)return0;for(int j =0; j <= mx; j ++){
sum[j]=(dp[j]+(j ? sum[j -1]:0))% mod;}for(int j =0; j <= mx; j ++){
dp[j]=((j >= L ? sum[j - L]:0)-(j >=(R +1)? sum[j -(R +1)]:0)+ mod)% mod;}}int ans =0;for(int i =1; i <= mx; i ++){
ans =(ans + dp[i])% mod;}return ans;}intmain(){scanf("%d %d",&n,&m);get_mu(m);for(int i =1; i <= n; i ++){scanf("%d %d",&l[i],&r[i]);}
ll ans =0;for(int i =1; i <= m; i ++){
ans =(ans +1LL* mu[i]*f(i)% mod + mod)% mod;}printf("%lld\n", ans);return0;}