The LCMs Must be Large | CF 1166E 给定一个
n
n
n 给定
m
m
m 组数据,每组数据是一个集合
S
S
S,每个集合的元素都在
[
1
,
n
]
[1,n]
[1,n] 的范围,且集合大小
<
n
<n
<n 问是否存在一个
a
n
a_n
an,满足对于每一组数据,都满足
L
C
M
(
S
)
>
L
C
M
(
I
−
S
)
LCM(S)>LCM(I-S)
LCM(S)>LCM(I−S) 也就是选中的集合的数字的最小公倍数大于其他没有选中的元素的最小公倍数。
1
≤
m
≤
50
1\le m\le 50
1≤m≤50
1
≤
n
≤
1
0
4
1\le n\le 10^4
1≤n≤104
思路
手膜一下,就会发现如果有两组数据满足他们的交是空集,那么一定没有答案。否则一定存在答案。 证明很好做,就当课后习题吧(X 我们可以通过构造法来证明答案存在。 我们先拿出
m
m
m 个质数
p
1
,
p
2
,
⋯
,
p
m
p_1,p_2,\cdots,p_m
p1,p2,⋯,pm 如果第
j
j
j 组数据包含
a
i
a_i
ai,那么我们让
a
i
a_i
ai 包含因子
p
j
p_j
pj 这样,
L
C
M
(
S
)
=
∏
i
=
1
m
p
i
LCM(S)=\prod_{i=1}^m p_i
LCM(S)=∏i=1mpi,且
L
C
M
(
I
−
S
)
<
L
C
M
(
S
)
LCM(I-S)<LCM(S)
LCM(I−S)<LCM(S) 因为它不包含质因子
p
j
p_j
pj 且这样的要求是满足任意两组数据的交集非空。
于是我们的问题就是如何判断交集非空。 暴力 for 每两组数据,每次用一个
v
i
s
vis
vis 判断、回撤即可。
代码
时间复杂度:
O
(
m
2
n
)
O(m^2n)
O(m2n)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =1e4+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;int num[MAX];
vector<int>V[MAX];intmain(){int m,n;scanf("%d%d",&m,&n);for(int i =1;i <= m;++i){int si;scanf("%d",&si);while(si--){int t;scanf("%d",&t);
V[i].push_back(t);}}for(int i =1;i <= m;++i)for(int j = i +1;j <= m;++j){
bool jiao = false;for(auto it : V[i])num[it]++;for(auto it : V[j])if(num[it])jiao = true;if(!jiao){puts("impossible");return0;}for(auto it : V[i])num[it]--;}puts("possible");return0;}
H:Calendar Ambiguity | CF 1389E
题意
Calendar Ambiguity | CF 1389E 一年有
m
m
m 个月,每个月有
d
d
d 天,每
w
w
w 天为一周。 问有多少对
(
x
,
y
)
(x,y)
(x,y),满足
x
<
y
x<y
x<y 且第
x
x
x 月的第
y
y
y 天和第
y
y
y 月的第
x
x
x 天是在一周的同一天。(比如都是周三,或者都是周
88
88
88 之类的)
1000
1000
1000 组样例
1
≤
m
,
d
,
w
≤
1
0
9
1\le m,d,w\le 10^9
1≤m,d,w≤109
思路
一个很裸的数学题。 列出式子,裸式子就是
(
x
−
1
)
d
+
y
≡
(
y
−
1
)
d
+
x
(
m
o
d
w
)
(x-1)d+y\equiv (y-1)d+x \pmod w
(x−1)d+y≡(y−1)d+x(modw) 化简完之后就变成
x
(
d
−
1
)
≡
y
(
d
−
1
)
(
m
o
d
w
)
x(d-1)\equiv y(d-1) \pmod w
x(d−1)≡y(d−1)(modw) 考虑到同余方程的除法操作,我们方程两边同时除以
d
−
1
d-1
d−1,模数要除以模数和除数的
gcd
\gcd
gcd
于是相当于我们要求
x
≡
y
(
m
o
d
p
)
x\equiv y\pmod p
x≡y(modp),
1
≤
x
<
y
≤
min
(
m
,
d
)
1\le x<y\le \min(m,d)
1≤x<y≤min(m,d) 的方案数 考虑条件太严苛。我们记
min
(
m
,
d
)
=
u
p
\min(m,d)=up
min(m,d)=up,我们可以先算出
1
≤
x
,
y
≤
u
p
1\le x,y\le up
1≤x,y≤up 的方案数 然后减去
x
=
y
x=y
x=y 的方案数(共
e
d
ed
ed 个),然后考虑对称性,除以二就是我们要求的答案
于是相当于我们要求
x
≡
y
(
m
o
d
p
)
x\equiv y\pmod p
x≡y(modp) ,
1
≤
x
,
y
≤
u
p
1\le x,y\le up
1≤x,y≤up 的方案数 我们按模
p
p
p 等价类划分。假设分成
k
+
1
k+1
k+1 个段,其中前面的段的范围是
[
1
,
p
]
,
[
p
+
1
,
2
p
]
,
⋯
[1,p],[p+1,2p],\cdots
[1,p],[p+1,2p],⋯ 最后一个段是
[
k
p
+
1
,
u
p
]
[kp+1,up]
[kp+1,up] 我们记
s
1
s1
s1 表示最后一个段的可选方案数。
s
2
s2
s2 表示
p
−
s
1
p-s1
p−s1 我们最终的答案就是
s
1
∗
(
k
+
1
)
2
+
s
2
∗
k
2
s1*(k+1)^2+s2*k^2
s1∗(k+1)2+s2∗k2,可以想一想为什么
代码
时间复杂度:
O
(
T
log
)
O(T\log)
O(Tlog)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =1e4+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;intmain(){int T;
cin >> T;while(T--){
ll m,d,w;cin >> m >> d >> w;
ll p = w /__gcd(w,d-1);
ll up=min(m,d);
ll k = up / p;
ll s1,s2;
s1 = up - k * p;
s2 = p - s1;
ll ans = s1 *(k +1)*(k +1)+ s2 * k * k;
ans -=min(m,d);
ans /=2;
cout << ans << endl;}return0;}
I:Kate and imperfection | CF 1333F
题意
Kate and imperfection | CF 1333F 给定一个集合
S
=
{
1
,
2
,
3
,
⋯
,
n
}
S=\{1,2,3,\cdots,n\}
S={1,2,3,⋯,n} 对于
I
k
I_k
Ik,表示你要从中选出
k
k
k 个元素,对于所有的在
I
k
I_k
Ik 的元素对
(
x
,
y
)
(x,y)
(x,y),你需要让
max
{
gcd
(
x
,
y
)
}
\max\{\gcd(x,y)\}
max{gcd(x,y)} 最小。 对于每一个
I
2
,
⋯
,
I
n
I_2,\cdots,I_n
I2,⋯,In,你都要求出这个最小值是多少
2
≤
n
≤
5
⋅
1
0
5
2\le n\le 5\cdot 10^5
2≤n≤5⋅105
思路
我们可以先假定
n
n
n 很大。假设有
p
p
p 个质数,那么我们优先选这些质数好了,
1
1
1 选了也对答案没有影响。这样,直到
I
p
+
1
I_{p+1}
Ip+1 仍然为
1
1
1
再大一些,我们就必须要把合数加进来了。假设合数的质因子分解为
a
∗
b
∗
c
a*b*c
a∗b∗c,且
a
<
b
<
c
a<b<c
a<b<c,那么它加进来的时候,此时
max
{
gcd
(
x
,
y
)
}
\max\{\gcd(x,y)\}
max{gcd(x,y)} 至少为
max
{
a
,
b
,
c
}
\max\{a,b,c\}
max{a,b,c}。 然后发现先加进来
4
4
4 一定是最优的。然后加
6
6
6 而不是加
8
8
8,因为
gcd
(
4
,
8
)
=
4
>
gcd
(
3
,
6
)
=
3
\gcd(4,8)=4>\gcd(3,6)=3
gcd(4,8)=4>gcd(3,6)=3 然后发现,我们新加进来的数字,加进来的最劣答案就是
x
/
m
i
n
_
p
r
i
m
e
x
x/min\_prime_x
x/min_primex
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =5e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;int tot;
bool vis[MAX];int pri[MAX];int mp[MAX];int ans[MAX];voidshai(int n){
mp[1]=1;for(int i =2;i <= n;++i){if(!vis[i]){
pri[++tot]= i;
mp[i]= i;}for(int j =1;j <= tot && i * pri[j]<= n;++j){
vis[i * pri[j]]=1;
mp[i * pri[j]]= pri[j];if(i % pri[j]==0){break;}}}for(int i =2;i <= n;++i){
ans[i]= i / mp[i];}sort(ans+2,ans+n+1);}intmain(){int n;scanf("%d",&n);shai(n);for(int i =2;i <= n;++i){printf("%d ",ans[i]);}return0;}
J:Infinite Path | CF 1327D
题意
J:Infinite Path | CF 1327D 给定一个全排列
p
n
p_n
pn 给定一个颜色数组
c
n
c_n
cn 给定一个记号
p
×
p
p\times p
p×p 也就是让
p
[
i
]
=
p
[
p
[
i
]
]
p[i]=p[p[i]]
p[i]=p[p[i]] 给定一个记号
p
k
=
∏
i
=
1
k
p
p^k=\prod_{i=1}^k p
pk=∏i=1kp 定义一条无穷路径也就是
p
[
i
]
,
p
[
p
[
i
]
]
,
p
[
p
[
p
[
i
]
]
]
,
⋯
p[i],p[p[i]],p[p[p[i]]],\cdots
p[i],p[p[i]],p[p[p[i]]],⋯
求出最小的
k
k
k,
k
≥
1
k\ge 1
k≥1,满足
p
k
p^k
pk 至少存在一条无穷路径,满足这条路径上的每个点的颜色都相同
1
≤
n
≤
2
⋅
1
0
5
1\le n\le 2\cdot 10^5
1≤n≤2⋅105
思路
(1)首先这个全排列一定组成一些环。我们每一个环都可以单独拿出来考虑。 (2)这个环,环长为
c
n
t
cnt
cnt,则
p
k
p^k
pk 产生的环数就是
gcd
(
k
,
c
n
t
)
\gcd(k,cnt)
gcd(k,cnt) 我们可以
f
o
r
for
for 每个环,再
f
o
r
for
for 每一个
k
k
k ,再去判断这个新的环是否里面的颜色都相同。 (3)但是貌似我们没必要
f
o
r
for
for 每一个
k
k
k,对于不同的
k
k
k 但是有相同的
gcd
\gcd
gcd,这些
k
k
k 我只需要选一个即可,因为他们都是等价的。于是,我们选择的
k
k
k 的数量降到了因子的数量级
O
(
c
n
t
1
/
3
)
O(cnt^{1/3})
O(cnt1/3) (4)我怎么快速知道
p
k
p^k
pk 的每一个点是什么? 我把一个环的点拿出来,记作
c
1
,
c
2
,
⋯
,
c
c
n
t
c_1,c_2,\cdots,c_{cnt}
c1,c2,⋯,ccnt
p
[
i
]
=
c
(
i
+
1
)
%
c
n
t
p[i]=c_{(i+1) \% cnt}
p[i]=c(i+1)%cnt
p
[
p
[
i
]
]
=
c
(
i
+
2
)
%
c
n
t
p[p[i]]=c_{(i+2) \% cnt}
p[p[i]]=c(i+2)%cnt
p
k
[
i
]
=
c
(
i
+
k
)
%
c
n
t
p^k[i]=c_{(i+k) \% cnt}
pk[i]=c(i+k)%cnt
代码
时间复杂度:
O
(
n
3
/
4
)
O(n^{3/4})
O(n3/4)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =5e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;int ans;int nxt[MAX];int col[MAX];
bool vis[MAX];
vector<int>V;int duo;int duovis[MAX];
bool sam;int t_per[MAX];voiddfs(int now,int c){if(col[now]!= c){sam = false;}
duovis[now]= duo;int nxxt = t_per[now];if(duovis[nxxt]!= duo)dfs(nxxt,c);}voidcheck(int cnt,int stp){
duo++;for(int i =0;i < cnt;++i){
t_per[V[i]]= V[(i+stp)%cnt];}for(int i =0;i < cnt;++i){int now = V[i];if(duovis[now]== duo)continue;
sam = true;dfs(now,col[now]);if(sam){
ans =min(ans,stp);return;}}}voidwork(int S){
V.clear();int now = S;while(!vis[now]){
vis[now]=1;
V.push_back(now);
now = nxt[now];}int cnt = V.size();for(int i =1;i * i <= cnt;++i){if(cnt % i ==0){check(cnt,i);check(cnt,cnt/i);}}}intmain(){int T;scanf("%d",&T);while(T--){int n;scanf("%d",&n);for(int i =0;i < n;++i)vis[i]=0;for(int i =0;i < n;++i)scanf("%d",&nxt[i]),nxt[i]--;for(int i =0;i < n;++i)scanf("%d",&col[i]);
ans = n;for(int i =0;i < n;++i){if(vis[i])continue;work(i);}printf("%d\n",ans);}return0;}
K:Multiples and Power Differences | CF 1485D
题意
Multiples and Power Differences | CF 1485D 我的构造是真的垃圾… 给定一个矩阵
a
n
,
m
a_{n,m}
an,m 你需要构造一个矩阵
b
n
,
m
b_{n,m}
bn,m,满足以下要求: (1)
a
i
,
j
∣
b
i
,
j
a_{i,j}|b_{i,j}
ai,j∣bi,j (2)对于
b
b
b 中边相邻的两个数值,他们的差是某个正整数的四次方。 (3)
1
≤
b
i
,
j
≤
1
0
6
1\le b_{i,j}\le 10^6
1≤bi,j≤106
2
≤
n
,
m
≤
500
2\le n,m\le 500
2≤n,m≤500
1
≤
a
i
,
j
≤
16
1\le a_{i,j}\le 16
1≤ai,j≤16
思路
差是某个正整数的四次方?这能做? 好吧,有一个很神奇的提示:
l
c
m
(
1
,
2
,
3
,
⋯
,
16
)
=
720720
lcm(1,2,3,\cdots,16)=720720
lcm(1,2,3,⋯,16)=720720 这个数字
<
1
0
6
<10^6
<106
且答案非常稀少,暴力搜索剪枝也是过不了的。接下来就是神奇的构造 如果
i
+
j
i+j
i+j 是个奇数,那么
b
i
,
j
=
720720
b_{i,j}=720720
bi,j=720720 否则,
b
i
,
j
=
720720
+
a
i
,
j
4
b_{i,j}=720720+a_{i,j}^4
bi,j=720720+ai,j4 哇,看了之后发现好简单,但很难一下子想到…
代码
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =5e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;intmain(){int n,m;cin >> n >> m;for(int i =1;i <= n;++i){for(int j =1;j <= m;++j){int t;cin >> t;if((i+j)&1)cout <<720720<<" ";else cout <<720720+ t * t * t * t <<" ";}puts("");}return0;}