AtCoder Beginner Contest 248
A - Lacked Number
题意:
给出10个数字,输出0~9中未出现的那个数字。
思路:
统计数字出现情况即可。
时间复杂度:
O
(
1
)
O(1)
O(1)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 2e5+10,M = 2e5+10,INF = 0x3f3f3f3f,mod = 32768;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int cnt[10] = {0};
std::string s;
std::cin>>s;
for(auto c : s)cnt[c-'0']++;
for(int i = 0 ; i < 10 ; i++)if(!cnt[i])std::cout<<i;
return 0;
}
B - Slimes
题意:
给定
A
,
B
,
K
A,B,K
A,B,K三个数字,每次操作可以使
A
=
A
?
K
A=A*K
A=A?K,输出
A
≥
B
A\geq B
A≥B的最小次数
思路:
直接模拟即可。
时间复杂度:
O
(
B
A
K
)
O(\frac{B}{AK})
O(AKB?)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 2e5+10,M = 2e5+10,INF = 0x3f3f3f3f,mod = 32768;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
ll a,b,k;
std::cin>>a>>b>>k;
int cnt = 0;
while(b > a)cnt++,a *= k;
std::cout<<cnt;
return 0;
}
C - Dice Sum
题意:
现要求找到一个长度为
N
N
N的序列,对于其中的每一个元素,满足如下要求:
-
1
≤
A
i
≤
M
1\leq A_i\leq M
1≤Ai?≤M
-
∑
i
=
1
N
A
i
≤
K
\sum _{i=1}^{N}A_i\leq K
∑i=1N?Ai?≤K
询问满足要求的序列个数。
思路:
考虑动态规划
设有
d
p
[
N
]
[
K
]
dp[N][K]
dp[N][K]:
- 第一维表示当前序列长度
- 第二维表示当前序列的和
- 数组的值表示方案数
枚举所有长度以及序列和,进行状态转移即可。
转移方程:
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
for(int o = i + j - 1; o <= k ; o++)
{
dp[i][o] = (dp[i][o] + dp[i-1][o-j])%mod;
}
时间复杂度:
O
(
N
K
)
O(NK)
O(NK)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 60,M = 2e5+10,INF = 0x3f3f3f3f,mod = 998244353;
ll dp[N][N*N];
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n,m,k;
std::cin>>n>>m>>k;
dp[0][0] = 1;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
for(int o = i + j - 1; o <= k ; o++)
{
dp[i][o] = (dp[i][o] + dp[i-1][o-j])%mod;
}
ll sum = 0;
for(int i = n ; i<= k ; i++)sum = (sum + dp[n][i])%mod;
std::cout<<sum;
return 0;
}
D - Range Count Query
题意:
给定一个长度为
n
n
n的序列,和
q
q
q个询问,询问格式如下:
给定
L
,
R
,
X
L,R,X
L,R,X,要求输出序列中
L
L
L到
R
R
R区间内值为$X的个数。
思路:
因为
1
≤
A
i
≤
N
1\leq A_i\leq N
1≤Ai?≤N,所以我们可以用一个数组,把每一个数字的所有下标分别存起来,然后用二分解决询问即可。
时间复杂度:
O
(
q
l
o
g
n
)
O(qlogn)
O(qlogn)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 2e5+10,M = 2e5+10,INF = 0x3f3f3f3f,mod = 998244353;
std::vector<int> v[N];
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n,q;
std::cin>>n;
for(int i = 1 ; i <= n ; i++)
{
int x;
std::cin>>x;
v[x].push_back(i);
}
std::cin>>q;
while(q--)
{
int l,r,x;
std::cin>>l>>r>>x;
auto it1 = std::lower_bound(v[x].begin(),v[x].end(),l);
if(it1==v[x].end())
{
std::cout<<0<<'\n';
continue;
}
auto it2 = std::upper_bound(v[x].begin(),v[x].end(),r);
if(it2==v[x].begin())
{
std::cout<<0<<'\n';
continue;
}
it2--;
std::cout<<(it2-it1+1)<<'\n';
}
return 0;
}
E - K-colinear Line
题意:
在坐标平面内存在
N
N
N个点,每个点的坐标为
(
X
i
,
Y
i
)
(X_i,Y_i)
(Xi?,Yi?),询问存在多少条不同的直线,穿过至少
K
K
K个点。
思路:
因为两点可以确定一条直线,而且点数
N
≤
300
N\leq 300
N≤300,所以可以进行如下处理:
- 两两枚举所有的点
- 在确定完一条直线后,枚举所有的点判断这条直线穿过了多少点
三个点在一条直线上,显然有
A
C
?
=
k
A
B
?
\vec{AC}=k \vec{AB}
AC
=kAB
y
3
?
y
2
x
3
?
x
2
=
y
3
?
y
1
x
3
?
x
1
?
(
y
3
?
y
2
)
(
x
3
?
x
1
)
=
(
x
3
?
x
2
)
(
y
3
?
y
1
)
\frac{y_3-y_2}{x_3-x_2}=\frac{y_3-y_1}{x_3-x_1}\Rightarrow(y_3-y_2)(x_3-x_1)=(x_3-x_2)(y_3-y_1)
x3??x2?y3??y2??=x3??x1?y3??y1???(y3??y2?)(x3??x1?)=(x3??x2?)(y3??y1?)
将所有满足条件的直线统计出来即可。
时间复杂度:
O
(
n
3
)
O(n^3)
O(n3)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 310,M = 2e5+10,INF = 0x3f3f3f3f,mod = 998244353;
struct Node{
ll x,y;
}a[N];
bool book[N][N];
bool OK(Node x,Node y,Node z)
{
return (z.y-y.y)*(z.x-x.x)==(z.y-x.y)*(z.x-y.x);
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int ans = 0;
int n,k;
std::cin>>n>>k;
if(k==1)
{
std::cout<<"Infinity";
return 0;
}
for(int i = 1 ; i <= n ; i++)std::cin>>a[i].x>>a[i].y;
for(int i = 1 ; i < n ; i++)
for(int j = i + 1 ; j <= n ; j++)
if(!book[i][j])
{
int cnt = 2;
std::vector<int> b;
for(int o = 1 ; o <= n ; o++)
{
if(o == i || o == j)continue;
if(OK(a[i],a[j],a[o]))
{
cnt++;
b.push_back(o);
}
}
b.push_back(i);
b.push_back(j);
for(auto x : b)
for(auto y : b)book[x][y] = true;
if(cnt >= k)ans++;
}
std::cout<<ans;
return 0;
}
F - Keep Connect
题意:
给出这样的一个连通图,对于每一个
i
(
1
≤
i
≤
n
?
1
)
i(1\leq i\leq n-1)
i(1≤i≤n?1),输出删除
i
i
i条边并且仍然保持连通性的方案数。
思路:
考虑动态规划
设有
d
p
[
i
]
[
j
]
[
2
]
dp[i][j][2]
dp[i][j][2]:
-
i
i
i表示前
i
i
i组点
-
j
j
j表示删除的边数
- 第三维记录的是第
i
i
i组点之间的连通性(第
i
i
i组的两个点当前能否连通)
- 数组值表示方案数
我们用三个
b
o
o
l
bool
bool值
a
,
b
,
c
a,b,c
a,b,c来代表三条边,表示这条边是否删除。
- 那么当第
i
?
1
i-1
i?1组点是不连通的时候,显然是要保证
a
,
b
a,b
a,b两条边都相连,否则整张图必然失去连通性。
- 当第
i
?
1
i-1
i?1组点是连通的时候,我们只需要保证
a
,
b
a,b
a,b有一条边是连通的即可,如果
a
,
b
,
c
a,b,c
a,b,c三边中相连了至少两条边,那么就可以保证第
i
i
i组点是连通的。
有转移方程
if(!k)
{
if(!a||!b)continue;
dp[i+1][j+1-c][c] = (dp[i+1][j+1-dp[i][j][k])%Mod;
}
else
{
if(!a&&!b)continue;
dp[i+1][j+3-a-b-c][(a+b+c)>=2] = [j+3-a-b-c][(a+b+c)>=2] + dp[i%Mod;
}
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 3010,M = 2e5+10,INF = 0x3f3f3f3f,mod = 998244353;
ll dp[N][N][2];
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
dp[1][0][1] = dp[1][1][0] = 1;
int n;
ll Mod;
std::cin>>n>>Mod;
for(int i = 1 ; i < n ; i++)
for(int j = 0 ; j <= n ; j++)
for(int k = 0 ; k < 2 ; k++)
if(dp[i][j][k])
for(int a = 0 ; a < 2 ; a++)
for(int b = 0 ; b < 2 ; b++)
for(int c = 0 ; c < 2 ; c++)
{
if(!k)
{
if(!a||!b)continue;
dp[i+1][j+1-c][c] = (dp[i+1][j+1-c][c] + dp[i][j][k])%Mod;
}
else
{
if(!a&&!b)continue;
dp[i+1][j+3-a-b-c][(a+b+c)>=2] = (dp[i+1][j+3-a-b-c][(a+b+c)>=2] + dp[i][j][k])%Mod;
}
}
for(int i = 1 ; i < n ; i++)std::cout<<dp[n][i][1]<<' ';
return 0;
}
Ex - Beautiful Subsequences
题意:
给定一个长度为
n
n
n的全排列,询问满足如下条件的区间个数:
-
1
≤
L
≤
R
≤
N
1\leq L\leq R\leq N
1≤L≤R≤N
-
m
a
x
(
P
L
,
…
,
P
R
)
?
m
i
n
(
P
L
,
…
,
P
R
)
≤
R
?
L
+
K
max(P_L,\dots,P_R)-min(P_L,\dots,P_R)\leq R-L+K
max(PL?,…,PR?)?min(PL?,…,PR?)≤R?L+K
思路:
线段树+单调栈:
- 线段树:我们先固定一个
R
R
R,然后尝试去维护一个序列,这个序列中的第
i
i
i个点记录的值为
m
a
x
(
P
i
,
…
,
P
R
)
?
m
i
n
(
P
i
,
…
,
P
R
)
?
R
+
i
max(P_i,\dots,P_R)-min(P_i,\dots,P_R)-R+i
max(Pi?,…,PR?)?min(Pi?,…,PR?)?R+i,如果这个值
v
a
l
≤
K
val\leq K
val≤K,那么这个点所代表的区间就是一个合法区间,此外,因为这个序列是全排列,所以不难发现
v
a
l
val
val不会是负数。我们用线段树去维护这一段区间,区间内记录
v
a
l
val
val的最小值,并且记录下
v
a
l
+
1
,
v
a
l
+
2
,
…
,
v
a
l
+
k
val+1,val+2,\dots,val+k
val+1,val+2,…,val+k的点的个数。
然后就可以通过根节点的信息推知答案。
- 单调栈:我们可以通过单调栈动态的确定区间最值,确定完最值以后通过线段树区间修改即可。
时间复杂度:
O
(
N
K
l
o
g
N
)
O(NKlogN)
O(NKlogN)
AC代码:
#include<bits/stdc++.h>
typedef long long ll;
const int N = 150000,M = 2e5+10,INF = 0x3f3f3f3f,mod = 998244353;
struct Node{
int l,r;
int val,add,cnt[4];
}tr[N*4];
int w[N],k;
void pushup(int u)
{
tr[u].val = std::min(tr[u<<1].val,tr[u<<1|1].val);
for(int i = 0 ; i <= k ; i++)
{
tr[u].cnt[i] = 0;
int temp = tr[u].val - tr[u<<1].val + i;
if(temp >= 0)tr[u].cnt[i] += tr[u<<1].cnt[temp];
temp = tr[u].val - tr[u<<1|1].val + i;
if(temp >= 0)tr[u].cnt[i] += tr[u<<1|1].cnt[temp];
}
}
void pushdown(int u)
{
if(tr[u].add)
{
tr[u<<1].val += tr[u].add;
tr[u<<1|1].val += tr[u].add;
tr[u<<1].add += tr[u].add;
tr[u<<1|1].add += tr[u].add;
tr[u].add = 0;
}
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u] = {l,r};
tr[u].cnt[0] = 1;
tr[u].val = INF;
}
else
{
tr[u] = {l,r};
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
}
void modify(int u,int l,int r,int v)
{
if(l > r)return;
if(tr[u].l >= l && tr[u].r <= r)
{
tr[u].val += v;
tr[u].add += v;
}
else
{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)modify(u<<1,l,r,v);
if(r > mid)modify(u<<1|1,l,r,v);
pushup(u);
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int n;
std::cin>>n>>k;
for(int i = 1 ; i <= n ; i++)std::cin>>w[i];
build(1,1,n);
std::stack<std::pair<std::pair<int,int>,int>> stk1,stk2;
ll ans = 0;
for(int i = 1 ; i <= n ; i++)
{
modify(1,i,i,-INF);
modify(1,1,i-1,-1);
int x1 = i,x2 = i;
while(!stk1.empty())
{
if(w[i] < stk1.top().second)
{
modify(1,stk1.top().first.first,stk1.top().first.second,stk1.top().second);
x1 = stk1.top().first.first;
stk1.pop();
}
else break;
}
while(!stk2.empty())
{
if(w[i] > stk2.top().second)
{
modify(1,stk2.top().first.first,stk2.top().first.second,-stk2.top().second);
x2 = stk2.top().first.first;
stk2.pop();
}
else break;
}
stk1.push({{x1,i},w[i]});
stk2.push({{x2,i},w[i]});
modify(1,x1,i,-w[i]);
modify(1,x2,i,w[i]);
for(int c = 0 ; c <= k ; c++)
if(tr[1].val + c <= k)ans += tr[1].cnt[c];
}
std::cout<<ans;
return 0;
}
欢迎访问个人博客https://skynesser.github.io/
|