ARC118
第一次做arc
场,被爆杀QAQ
ARC118A
ARC118A题意
ARC国家的消费税率是
t
t
t 。其中
t
t
t 是正整数。
ARC国家有整数屋。整数屋先生以不含税价格
A
A
A 日元处理着各个正整数
A
A
A,这个含税价格是
⌊
100
+
t
100
A
⌋
\lfloor\frac{100 + t}{100}A \rfloor
⌊100100+tA⌋ 日元。但是,对于实数
x
x
x,
⌊
x
⌋
\lfloor x \rfloor
⌊x⌋ 表示
x
x
x 以下的最大整数。
虽然是经营所有正整数的整数屋,但存在不作为含税价格出现的正整数金额。在这些金额中,请找从小到大的第 N N N 个。
简要题意:
定义函数
f
(
A
)
=
A
+
⌊
t
∗
A
100
⌋
f(A)=A+\lfloor \frac{t*A}{100}\rfloor
f(A)=A+⌊100t∗A⌋
定义集合
S
=
{
A
∈
Z
∣
f
(
A
)
}
S=\{A\in \mathbb{Z}|f(A)\}
S={A∈Z∣f(A)}
求第
N
N
N 个没出现在集合
S
S
S 的正整数。
ARC118A题解
不难发现,函数
f
(
A
)
f(A)
f(A) 是由两个函数
f
1
(
A
)
=
A
f_1(A)=A
f1(A)=A 和
f
2
(
A
)
=
⌊
t
∗
A
100
⌋
f_2(A)=\lfloor \frac{t*A}{100}\rfloor
f2(A)=⌊100t∗A⌋ 组成的,第一个函数在
Z
\mathbb{Z}
Z 上连续,第二个每次越过
100
k
(
k
∈
Z
)
100k(k\in \mathbb{Z})
100k(k∈Z) 之后都会突变
1
1
1,故每一个没出现在
S
S
S 中的数字都满足
f
2
(
A
−
1
)
+
1
=
f
2
(
A
)
f_2(A-1) + 1 = f_2(A)
f2(A−1)+1=f2(A)。
不难推出,第
N
N
N 个没出现的数字就是
f
(
x
)
−
1
,
x
=
⌈
100
N
t
⌉
f(x)-1,x=\lceil \frac{100N}{t}\rceil
f(x)−1,x=⌈t100N⌉。
没了。
ARC118A代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x= 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
int t, N;
int A;
signed main(){
t = read();N = read();
A = (100LL * N) / t + ((100ll * N) % t ? 1ll : 0ll);
printf("%lld\n",A + (t * A) / 100ll - 1ll);
return 0;
}
ARC118B
link
当前用时:1:18:27.06
ARC118B题意
tips:不要看luogu翻译的解释。
给出一个长度为
k
k
k 的数列
a
a
a,其总和为
n
n
n,和另一个数字
m
m
m。
现让你构造长度同样为
k
k
k 的数列
b
b
b,满足以下要求:
- ∑ i = 1 k b i = m \sum_{i=1}^kb_i=m ∑i=1kbi=m
- max i = 1 k ∣ b i m − a i n ∣ \max_{i=1}^k{|\frac{b_i}{m}-\frac{a_i}{n}|} maxi=1k∣mbi−nai∣ 最小
给出构造方案。
ARC118B题解
看题看半天发现翻译没get到关键点
然后手写ceil又挂了(
首先通分第二个条件:
∣
b
i
m
−
a
i
n
∣
=
∣
b
i
×
n
−
a
i
×
m
n
×
m
∣
|\frac{b_i}{m}-\frac{a_i}{n}|=|\frac{b_i\times n-a_i\times m}{n\times m}|
∣mbi−nai∣=∣n×mbi×n−ai×m∣
发现上式变量只有
b
i
b_i
bi,于是二分
(
b
i
×
n
−
a
i
×
m
)
({b_i\times n-a_i\times m})
(bi×n−ai×m) 的最小值。
发现
b
i
∈
[
⌈
a
i
×
m
−
m
i
d
n
⌉
,
⌊
a
i
×
m
+
m
i
d
n
⌋
]
b_i\in[\lceil\frac{a_i\times m-mid}{n}\rceil,\lfloor\frac{a_i\times m+mid}{n}\rfloor]
bi∈[⌈nai×m−mid⌉,⌊nai×m+mid⌋],然后判断并构造一组解即可。
注意
c
e
i
l
ceil
ceil 的写法啊(恼
ARC118B代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x= 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e5 + 10;
int k, n, m,a[maxn], b[maxn], c[maxn], l[maxn], r[maxn];
bool check(int mid){
int suml = 0, sumr = 0;
for(int i = 1;i <= k;i++){
l[i] = ceil(1.0 * (a[i] * m - mid) / n);
r[i] = (a[i] * m + mid) / n;
if(l[i] < 0)l[i] = 0; c[i] = 0;
suml += l[i];sumr += r[i];
}
int rest = m - suml;
// printf("rest = %lld,",rest);
// printf("mid = %lld\n",mid);
// for(int i = 1;i <= k;i++){printf("l[%lld]=%lld r[%lld]=%lld\n",i,l[i],i,r[i]);}
if(!(suml <= m && m <= sumr))return false;
for(int i = 1;i <= k;i++){
c[i] = l[i] + min(r[i] - l[i],rest);
rest -= (c[i] - l[i]);
}
return rest == 0;
}
signed main(){
k = read(); n = read(); m = read();
for(int i = 1;i <= k;i++){a[i] = read();}
int L = 0, R = 1e9, mid = 0;
while(L <= R){
mid = L + R >> 1;
if(check(mid)){
for(int i = 1;i <= k;i++)b[i] = c[i];
// printf("mid = %lld\n",mid);
R = mid - 1;
}
else L = mid + 1;
}
// check(60);
for(int i = 1;i <= k;i++)printf("%lld ",b[i]);
return 0;
}
ARC118C
当前用时:2:10:15.38
ARC118C题意
构造一个长度为 n n n 的数列 a a a,满足:
- ∀ 1 ≤ i ≤ n , 1 ≤ a i ≤ 1 0 4 \forall 1\le i\le n,1\le a_i\le10^4 ∀1≤i≤n,1≤ai≤104
- ∀ 1 ≤ i , j ≤ n , gcd ( i , j ) > 1 \forall 1\le i,j\le n,\gcd(i,j)>1 ∀1≤i,j≤n,gcd(i,j)>1 且当 i ≠ j i\neq j i=j 时 a i ≠ a j a_i\neq a_j ai=aj
- gcd ( a 1 , a 2 , … , a n ) = = 1 \gcd(a_1,a_2,\dots,a_n)==1 gcd(a1,a2,…,an)==1
给出构造方案。
ARC118C题解
不难想到方法。
构造多列数字形如:
- 2 × 3 × 1 , 2 × 3 × 2 , … , 2 × 3 × k 2\times3\times1,2\times3\times2,\dots,2\times3\times k 2×3×1,2×3×2,…,2×3×k
- 2 × 5 × 1 , 2 × 5 × 2 , … , 2 × 5 × k 2\times5\times1,2\times5\times2,\dots,2\times5\times k 2×5×1,2×5×2,…,2×5×k
- ⋯ \dotsb ⋯
- 2 × p r i m e t × 1 , 2 × p r i m e t × 2 , … , 2 × p r i m e t × k 2\times prime_t\times1,2\times prime_t\times2,\dots,2\times prime_t\times k 2×primet×1,2×primet×2,…,2×primet×k
直到数字数量为
n
−
1
n-1
n−1。
然后令
a
n
=
∏
i
=
1
t
p
r
i
m
e
i
a_n=\prod_{i=1}^tprime_i
an=∏i=1tprimei。
然后就能保证满足上述条件。
证明略,可以对拍。
不过注意重复元素不能重复用。
ARC118C代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x= 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e4 + 10;
int n;
int prime[maxn], tot, a[maxn];
bool book[maxn];
signed main(){
n = read();
for(int i = 2;i < maxn;i++){
if(!book[i])prime[++tot] = i;
for(int j = 1;j <= tot && prime[j] * i < maxn;j++){
book[prime[j] * i] = 1;
if(i % prime[j] == 0)break;
}
}
memset(book,0,sizeof(book));
int tmp = 1,pim = 1, point = 1;
while(point < n){
pim++;tmp *= prime[pim];
int cnt = 2 * prime[pim], i = 0;
while(cnt * (i + 1) <= 1e4 && point < n){
i++;
if(book[cnt * i])continue;
a[point++] = i * cnt;
}
}
a[n] = tmp * (pim == 2 ? prime[pim + 1] : 1);
a[n - 1] = (pim == 2 ? (prime[pim + 1] * 2) : a[n - 1]);
for(int i = 1;i <= n;i++)printf("%d ",a[i]);
return 0;
}
ARC118D
ARC118D题意
给定质数 P P P 和两个正整数 a , b a,b a,b,要求构造一个长度为 P P P 的序列满足:
- 1 ≤ A i ≤ P − 1 1\le A_i\le P-1 1≤Ai≤P−1;
- A 1 = A P = 1 A_1=A_P=1 A1=AP=1;
- ( A 1 , A 2 , … , A P − 1 ) (A_1,A_2,\dots,A_{P-1}) (A1,A2,…,AP−1) 是一个 1 ∼ P − 1 1\sim P-1 1∼P−1 的排列;
-
∀
2
≤
i
≤
P
\forall 2\le i\le P
∀2≤i≤P,满足下列四个条件中的至少一个:
- A i ≡ a A i − 1 ( m o d P ) A_i\equiv aA_{i-1}\pmod P Ai≡aAi−1(modP);
- A i − 1 ≡ a A i ( m o d P ) A_{i-1}\equiv aA_i\pmod P Ai−1≡aAi(modP);
- A i ≡ b A i − 1 ( m o d P ) A_i\equiv bA_{i-1}\pmod P Ai≡bAi−1(modP);
- A i − 1 ≡ b A i ( m o d P ) A_{i-1}\equiv bA_i\pmod P Ai−1≡bAi(modP)。
Data Range : 2 ≤ P ≤ 1 0 5 , 1 ≤ a , b ≤ P − 1 \texttt{Data Range}:2\le P\le 10^5,1\le a,b\le P-1 Data Range:2≤P≤105,1≤a,b≤P−1, P P P 为质数。
ARC118D题解
不难发现,如果将每个数字
x
x
x 看做一个点,那么
x
x
x 会连向
x
×
a
(
m
o
d
p
)
x\times a\pmod p
x×a(modp),
x
×
a
−
1
(
m
o
d
p
)
x\times a^{-1}\pmod p
x×a−1(modp),
x
×
b
(
m
o
d
p
)
x\times b\pmod p
x×b(modp),
x
×
b
−
1
(
m
o
d
p
)
x\times b^{-1}\pmod p
x×b−1(modp) 这四个点。
然后再做下去就是哈密顿回路是NP问题,然后就没有思路了。
去看了下官方题解发现,可以根据这个构造一个矩阵,在矩阵上可以 O ( n ) O(n) O(n) 找回路。(真的是太巧妙了)
具体而言,令
n
n
n 是满足
a
n
≡
1
(
m
o
d
p
)
a^n\equiv 1\pmod p
an≡1(modp) 的最小正整数。
然后得到一个集合
S
=
{
x
∣
x
=
a
i
,
i
∈
[
0
,
n
]
}
S = \{x|x=a^i,i\in[0,n]\}
S={x∣x=ai,i∈[0,n]}。
然后找到最小的正整数
m
m
m 满足
b
m
(
m
o
d
p
)
∈
S
b^m\pmod p\in S
bm(modp)∈S。
首先判断
n
×
m
=
p
−
1
n\times m=p-1
n×m=p−1,如果不等无解。
然后建立一个
n
×
m
n\times m
n×m 的矩阵,其中每一个位置
(
i
,
j
)
(i,j)
(i,j) 填入
a
i
−
1
×
b
j
−
1
a^{i-1}\times b^{j-1}
ai−1×bj−1,然后就可以从
(
1
,
1
)
(1,1)
(1,1) 出发,走遍所有的格点,然后就没了。
证明?
- 每个
[
1
,
p
−
1
]
[1,p-1]
[1,p−1] 的数字在矩阵中出现至少一次:
- 不妨设一个数字 X = a x b y X=a^xb^y X=axby,则 X = a x ( b m ) t b r X = a^x(b^m)^tb^r X=ax(bm)tbr,其中 y ≡ r ( m o d m ) y\equiv r\pmod m y≡r(modm),故一定有 r < b r<b r<b。
- 又因为 ∃ k , b m = a k \exist k,b^m=a^k ∃k,bm=ak,则 X = a x a k t b r = a x + k t b r X=a^xa^{kt}b^r=a^{x+kt}b^r X=axaktbr=ax+ktbr。
- 但是由于 a i a^i ai 有循环节 n n n,故令 z ≡ x + k t ( m o d n ) z\equiv x+kt\pmod n z≡x+kt(modn),则 X = a z b r X=a^zb^r X=azbr。
- 此时 z + 1 ≤ n , r + 1 ≤ m z+1\le n,r+1\le m z+1≤n,r+1≤m,在矩阵中,证毕。
- 每个
[
1
,
p
−
1
]
[1,p-1]
[1,p−1] 的数字在矩阵中最多出现一次:
- 不妨设 X = a i b j = a x b y X=a^ib^j=a^xb^y X=aibj=axby 且 i ≠ x , j ≠ y i\neq x,j\neq y i=x,j=y。
- 那么一定有 a i − x ≡ b y − j ( m o d p ) a^{i-x}\equiv b^{y-j}\pmod p ai−x≡by−j(modp)。(如果 y < j y<j y<j 就两边都取逆元)
- 我们知道,前文定义中 ∃ k , b m = a k \exists k,b^m=a^k ∃k,bm=ak 且 ∀ i ∈ [ 1 , m ) , ∄ j , a j ≡ b i ( m o d p ) \forall i\in[1,m),\nexists j,a^j\equiv b^i\pmod p ∀i∈[1,m),∄j,aj≡bi(modp)。
- 而 1 ≤ y − j < m 1\le y-j<m 1≤y−j<m。
- 故不存在这样两对 ( i , j ) (i,j) (i,j) 和 ( x , y ) (x,y) (x,y),使得上式被满足。
- 证毕。
特别的,这个矩阵可以从 ( n , k ) (n,k) (n,k) 走到 ( 1 , k ) (1,k) (1,k),但是不能从 ( k , m ) (k,m) (k,m) 走到 ( k , 1 ) (k,1) (k,1),找回路的时候需要注意。
ARC118D代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x= 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 1e6 + 10;
int qpow(int x,int a,int mod){
int res = 1;
while(a){
if(a & 1) res = res * x % mod;
x = x * x % mod;a >>= 1;
}
return res;
}
int p, a, b, inva, invb;
bool book[maxn];
vector<int> arr;
signed main(){
p = read(); a = read(); b = read();
inva = qpow(a,p - 2,p);invb = qpow(b,p - 2,p);
int n = 1, m = 1, x = a, y = b;
while(n <= p){book[x] = 1;if(x == 1)break;x = (x * a % p);n++;}
while(m <= p){if(book[y])break;y = y * b % p;m++;}
if(n * m != p - 1 || !book[y]){puts("No");return 0;}
puts("Yes");
if(n == 1 || m == 1){
if(n == 1){swap(a, b);}x = 1;
for(int i = 1;i <= p;i++){printf("%lld ",x);x = x * a % p;}
puts(""); return 0;
}
if((m & 1) == 0){
x = 1;
for(int i = 1;i <= m;i++){
arr.push_back(x);
x = x * b % p;
}
x = x * invb % p;x = x * a % p;
for(int i = 1;i <= m;i += 2){
for(int j = 2;j <= n;j++){
arr.push_back(x);
x = x * a % p;
}
x = x * inva % p;x = x * invb % p;
for(int j = 2;j <= n;j++){
arr.push_back(x);
x = x * inva % p;
}
x = x * a % p;x = x * invb % p;
}
for(int i : arr)printf("%lld ",i);
puts("1");
return 0;
}
else{
swap(a,b);swap(inva,invb);swap(n, m);
x = 1, y = 1;
for(int i = 1;i <= m;i += 2){
for(int j = 1;j <= n;j++){
arr.push_back(x * y % p);
x = x * a % p;
}
y = y * b % p;x = x * inva % p;
for(int j = 1;j <= n;j++){
arr.push_back(x * y % p);
x = x * inva % p;
}
x = x * a % p;y = y * b % p;
}
for(int i : arr)printf("%lld ",i);
puts("1");
}
return 0;
}
ARC118E
ARC118E题意
给出一个
(
n
+
2
)
×
(
n
+
2
)
(n+2)\times(n+2)
(n+2)×(n+2) 的网格图。
定义一个排列
P
P
P 和它的一个函数
f
(
P
)
f(P)
f(P)。
其中
f
(
P
)
f(P)
f(P) 表示,将所有
(
i
,
P
i
)
(i,P_i)
(i,Pi) 点标记,则
f
(
P
)
f(P)
f(P) 等于所有从
(
0
,
0
)
→
(
n
+
1
,
n
+
1
)
(0,0)\to (n+1,n+1)
(0,0)→(n+1,n+1) 且不经过任意一个标记点的方案数。
现在这个排列有些位置没有填好(对应的
P
i
=
−
1
P_i=-1
Pi=−1),问所有可能填好的排列情况的
f
(
P
′
)
f(P')
f(P′) 的总和是多少。
ARC118E题解
首先先想一个问题,如果给出一个填好的排列,怎么算
f
(
P
)
f(P)
f(P)。
这里给出一个trick
,利用容斥来计数。
具体而言,让一条路径的贡献是
(
−
1
)
经过标记点的数量
(-1)^{经过标记点的数量}
(−1)经过标记点的数量,证明显然。
然后对于填好的序列,设
f
i
,
j
f_{i,j}
fi,j 表示到点
(
i
,
j
)
(i,j)
(i,j) 的方案数,则
(
−
1
)
l
i
m
i
,
j
×
f
i
,
j
→
f
i
+
1
,
j
(-1)^{lim_{i,j}}\times f_{i,j}\to f_{i+1,j}
(−1)limi,j×fi,j→fi+1,j 和
f
i
,
j
+
1
f_{i,j+1}
fi,j+1。
那对于没填好的序列呢,怎么办?
统计下未确定的点的总数为
m
m
m。
设
f
i
,
j
,
k
,
a
,
b
f_{i,j,k,a,b}
fi,j,k,a,b 表示到点
(
i
,
j
)
(i,j)
(i,j),已经钦定了
k
k
k 个未确定的标记点的位置,且当前状态下行
i
i
i (
a
=
0
⇔
a=0\Leftrightarrow
a=0⇔ 没有标记点 /
a
=
1
⇔
a=1\Leftrightarrow
a=1⇔ 有标记点),列
j
j
j (
b
=
0
⇔
b=0\Leftrightarrow
b=0⇔ 没有标记点 /
b
=
1
⇔
b=1\Leftrightarrow
b=1⇔ 有标记点)的方案数。
显然有
+
f
i
,
j
,
k
,
a
,
b
→
f
i
+
1
,
j
,
k
,
r
o
w
i
+
1
,
b
+f_{i,j,k,a,b}\to f_{i+1,j,k,row_{i+1},b}
+fi,j,k,a,b→fi+1,j,k,rowi+1,b
+
f
i
,
j
,
k
,
a
,
b
→
f
i
,
j
+
1
,
k
,
a
,
l
i
n
e
j
+
1
+f_{i,j,k,a,b}\to f_{i,j+1,k,a,line_{j+1}}
+fi,j,k,a,b→fi,j+1,k,a,linej+1
当
a
=
0
,
b
=
0
a=0,b=0
a=0,b=0 时
−
f
i
,
j
,
k
,
a
,
b
→
f
i
+
1
,
j
,
k
+
1
,
r
o
w
i
+
1
,
1
-f_{i,j,k,a,b}\to f_{i+1,j,k+1,row_{i+1},1}
−fi,j,k,a,b→fi+1,j,k+1,rowi+1,1
−
f
i
,
j
,
k
,
a
,
b
→
f
i
,
j
+
1
,
k
+
1
,
1
,
l
i
n
e
j
+
1
-f_{i,j,k,a,b}\to f_{i,j+1,k+1,1,line_{j+1}}
−fi,j,k,a,b→fi,j+1,k+1,1,linej+1
最后答案
a
n
s
=
∑
i
=
0
m
f
n
+
1
,
n
+
1
,
i
,
0
,
0
×
(
m
−
i
)
!
ans=\sum_{i=0}^mf_{n+1,n+1,i,0,0}\times (m-i)!
ans=i=0∑mfn+1,n+1,i,0,0×(m−i)!
没有然后了。
浇浇计数题QWQ
ARC118E代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x= 0, f= 1;char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
return x * f;
}
const int maxn = 210, mod = 998244353;
int f[maxn][maxn][maxn][2][2];
int n, a[maxn], m,fac[maxn];
bool lim[maxn][maxn],line[maxn],row[maxn];
signed main(){
fac[0] = 1; n = read();
for(int i = 1;i <= n;i++)a[i] = read();
for(int i = 1;i <= n;i++){
fac[i] = fac[i - 1] * i % mod;
if(a[i] != -1){
m++; lim[i][a[i]] = line[a[i]] = row[i] = 1;
}
}
m = n - m;int *x;
f[0][0][0][1][1] = 1;
for(int i = 0;i <= n + 1;i++)for(int j = 0;j <= n + 1;j++)
for(int k = 0;k <= m;k++)
for(int a = 0;a < 2;a++)for(int b = 0;b < 2;b++){
int t = f[i][j][k][a][b];if(!t || lim[i][j])continue;
if(!lim[i + 1][j]){
x = &f[i + 1][j][k][row[i + 1]][b];
*x += t;if(*x > mod)*x -= mod;
}
if(!lim[i][j + 1]){
x = &f[i][j + 1][k][a][line[j + 1]];
*x += t;if(*x > mod)*x -= mod;
}
if(a || b)continue;
if(!lim[i + 1][j]){
x = &f[i + 1][j][k + 1][row[i + 1]][1];
*x -= t;if(*x < 0)*x += mod;
}
if(!lim[i][j + 1]){
x = &f[i][j + 1][k + 1][1][line[j + 1]];
*x -= t;if(*x < 0)*x += mod;
}
}
int ans = 0;
for(int i = 0;i <= m;i++){ans = (ans + f[n + 1][n + 1][i][0][0] * fac[m - i]) % mod;}
printf("%lld\n",ans);
return 0;
}