第一次AK div2,有点小开心。
A. Display The Number
题目大意:
给
n
n
n个火柴棍,求能组成的最大的数字是多少。
解题思路:
贪心,如果是奇数那就第一位为7后面都是1,否则全是1.
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;cin>>T;
while(T--){
int n; cin>>n;
if(n&1){
if(n == 1) printf("1"), n = 0;
else n -= 3, printf("7");
}
while(n) printf("1"), n -= 2;
printf("\n");
}
}
B. Infinite Prefixes
题目大意:
给一个01串
s
s
s,无穷个串
s
s
s组成串
t
t
t,求
t
t
t有多少个前缀使得串中balance = 0的个数-1的个数=
x
x
x
解题思路:
先求出整个
s
s
s的balance,然后对于
s
s
s的每个前缀判断一下是否有可能在加上若干个
s
s
s之后
b
a
l
a
n
c
e
=
x
balance=x
balance=x。要特判
s
s
s的balance等于0的情况。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 50;
char s[maxn];
int n, x;
int main()
{
int T; cin>>T;
while(T--){
scanf("%d%d", &n, &x); scanf("%s", s);
int ans = 0, g = 0;
for(int i = 0; i < n; ++i){
if(s[i] == '0') g++;
else g--;
}
int cur = 0;
if(g == 0){
if(x == 0) {
cout<<-1<<endl; continue;
}
for(int i = 0; i < n; ++i){
if(s[i] == '0') cur++;
else cur--;
if(cur == x){ans = -1; break;}
}
cout<<ans<<endl; continue;
}
else{
if(cur == x) ans++;
for(int i = 0; i < n; ++i){
if(s[i] == '0') cur++;
else cur--;
if((x-cur)%g == 0 && ((x-cur)/g >= 0) ) ans++;
}
cout<<ans<<endl;
}
}
}
C. Obtain The String
题目大意:
给两个串
s
s
s和
t
t
t,每次操作从
s
s
s中取出一个子序列消掉
t
t
t的某前缀。问最少几次操作把
t
t
t消完。
解题思路:
直接贪心的消除就好,每次匹配到没有能匹配的就从头开始并让答案加1.
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 50;
int nxt[maxn][26];
char s[maxn];
char t[maxn];
int main()
{
int T;cin>>T;
while(T--){
scanf("%s", s+1);
int n = strlen(s+1);
scanf("%s", t+1);
int len = strlen(t+1);
for(int i = 0; i < 26; ++i) nxt[n][i] = 0;
for(int i = n-1; i >= 0; --i){
for(int j = 0; j < 26; ++j) nxt[i][j] = nxt[i+1][j];
nxt[i][s[i+1]-'a'] = i+1;
}
int ans = 1;
int cur = 0;
for(int i = 1; i <= len; ++i){
int x = t[i]-'a';
if(!nxt[0][x]){
ans = -1;break;
}
if(nxt[cur][x]){
cur = nxt[cur][x];
}else{
cur = nxt[0][x];
ans++;
}
}
cout<<ans<<endl;
}
}
D. Same GCDs
题目大意:
给出
a
a
a和
m
m
m,求满足
g
c
d
(
a
,
m
)
=
g
c
d
(
a
+
x
,
m
)
gcd(a,m)=gcd(a+x,m)
gcd(a,m)=gcd(a+x,m)且
x
∈
[
0
,
m
−
1
]
x\in [0,m-1]
x∈[0,m−1]的
x
x
x的个数。
a
,
m
≤
1
0
10
a,m\le10^{10}
a,m≤1010
解题思路:
设
p
=
g
c
d
(
a
,
m
)
p=gcd(a,m)
p=gcd(a,m),易得
x
x
x为
p
p
p的倍数。设
a
=
u
∗
p
,
m
=
v
∗
p
a=u*p, m=v*p
a=u∗p,m=v∗p,
x
=
x
′
∗
p
x=x'*p
x=x′∗p,问题转换成了
[
u
,
u
+
m
p
−
1
]
[u,u+\frac{m}{p}-1]
[u,u+pm−1]范围内有多少个数字与
m
p
\frac{m}{p}
pm互质。
把
m
p
\frac{m}{p}
pm的质因子提出来之后容斥求一下就好了。
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
vector<ll> v;
int bin[1<<20];
ll work(ll a){
ll ans = a;
int n = v.size();
for(int mask = 1; mask < (1<<n); ++mask){
ll t = 1;
for(int i = 0; i < n; ++i) if(mask>>i&1) t*=v[i];
ll f = 1; if(bin[mask]&1) f = -1;
ans += f*(a/t);
}return ans;
}
ll sol(ll a, ll b){
v.clear();
ll t = b;
for(ll p = 2; p*p <= b; ++p){
if(t%p == 0) v.push_back(p);
while(t%p == 0) t/= p;
}
if(t != 1) v.push_back(t);
return work(a+b-1)-work(a)+1;
}
int main()
{
for(int i = 1; i < (1<<20); ++i) bin[i] = bin[i-lowbit(i)]+1;
int T; cin>>T;
while(T--){
ll a, m;
cin>>a>>m;
ll p = __gcd(a, m);
a/=p; m/= p;
cout<<sol(a, m)<<endl;
}
}
E. Permutation Separation
题目大意:
给你一个排列,每个排列中的第
i
i
i个数字为
p
i
p_i
pi,权值为
w
i
w_i
wi。你可以选择一个位置切一刀把它分成左右两部分,然后你可以花费
w
i
w_i
wi把
p
i
p_i
pi移动到另一个部分。最终要保证左半部分的最大值小于右半部分的最小值。
解题思路:
先暴力的想:可以先枚举切的位置,再枚举右半部分的最小值是什么,这样我们有了一个
n
2
n^2
n2的算法,考虑优化这个算法。
从大到小枚举切的位置的时候,每次切的位置左移一位,相当于在右边加入了一个
p
i
p_i
pi,在左边去掉了一个
p
i
p_i
pi。那么右边最小值为
[
p
i
+
1
,
n
]
[p_i+1,n]
[pi+1,n]的这些答案都增加了
w
i
w_i
wi,为
[
1
,
p
i
]
[1,p_i]
[1,pi]的这些答案减少了
w
i
w_i
wi。
可以用线段树进行区间加法,维护区间最小值来维护答案。
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e5 + 50;
int p[maxn];
ll w[maxn];
ll lz[maxn<<2], mi[maxn<<2];
void down(int rt){
lz[rt<<1] += lz[rt];
mi[rt<<1] += lz[rt];
lz[rt<<1|1] += lz[rt];
mi[rt<<1|1] += lz[rt];
lz[rt] = 0;
return;
}
void up(int rt){
mi[rt] = min(mi[rt<<1], mi[rt<<1|1]);
}
void update(int rt, int l, int r, int L, int R, ll x){
if(L <= l && r <= R) {
lz[rt] += x;
mi[rt] += x;
return;
}down(rt);
if(L <= mid) update(lson, L, R, x);
if(R > mid) update(rson, L, R, x);
up(rt);
}
int n;
ll sol(){
for(int i = 1; i < n; ++i){
update(1, 0, n, 0, p[i], w[i]);
}
if(p[n] != n) update(1, 0, n, p[n]+1, n, w[n]);
ll ans = mi[1];
for(int i = n-1; i > 1; --i){
if(p[i] != n) update(1, 0, n, p[i]+1, n, w[i]);
update(1, 0, n, 0, p[i], -w[i]);
ans = min(ans, mi[1]);
}return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%d", &p[i]);
}
for(int i = 1; i <= n; ++i) scanf("%lld", &w[i]);
//reverse(p+1,p+1+n); reverse(w+1,w+1+n);
ll ans = sol();
ans = min(w[1], ans);
ans = min(w[n], ans);
cout<<ans<<endl;
}
F. Good Contest
题目大意:
给
n
n
n个数字,第
i
i
i个数字随机分布在
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]。求最后的序列单调不升的概率。
n
≤
50
n\le 50
n≤50
解题思路:
这题是Camp Day6的D题弱化版……
见2020 CCPC Wannafly Winter Camp Day6 D. 递增递增(DP)
这题思路就是把区间离散化之后
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个区间放了
j
j
j个数字的方案数。然后除以总方案数就是答案。
小号白嫖F题
#include<bits/stdc++.h>
#define ll long long
using namespace std;
/*注意爆long long*/
const int maxn = 205;
ll cc[200];
int num = 0;
int n;
ll l[55], r[55];
const ll mod = 998244353;
ll dp[200][200];
ll f[200][200];
ll fac[maxn], ifac[maxn], inv[maxn];
ll qm(ll a, ll b){ll res = 1; while(b) {if(b&1) res=res*a%mod; a = a*a%mod; b>>=1;} return res;}
ll Choose(ll a, ll b){//a个可选的数字,b个要放的数 C(b+a-1, b)
ll res = 1;
for(ll i = a; i < b+a; ++i) res = i%mod*res%mod;
for(ll i = 2; i <= b; ++i) res = res*inv[i]%mod;
return res;
}
int main()
{
fac[0] = ifac[0] = 1;
for(int i = 1; i < maxn; ++i) fac[i] = fac[i-1]*i%mod, ifac[i] = qm(fac[i], mod-2), inv[i] = qm(i, mod-2);
cin>>n;
ll fm = 1;
for(int i = 1; i <= n; ++i){
scanf("%lld%lld", &l[i], &r[i]); fm = fm*(r[i]-l[i]+1)%mod;
}
reverse(l+1,l+1+n);
reverse(r+1,r+1+n);
for(int i = 1; i <= n; ++i){
l[i] = max(l[i], l[i-1]);
cc[++num] = l[i];
}
cc[++num] = r[n]+1;
for(int i = n-1; i >= 1; --i){
r[i] = min(r[i], r[i+1]);
cc[++num] = r[i]+1;
}
sort(cc+1,cc+1+num);
num = unique(cc+1,cc+1+num)-cc-1;
ll inv2 = (mod+1)/2;
f[0][0] = 1;
for(int i = 1; i < num; ++i){
f[i][0] = 1;
ll L = cc[i], R = cc[i+1]-1;
for(int j = 1; j <= n; ++j){
f[i][j] = f[i-1][j];
for(int k = j; k > 0; --k){
if(l[k] <= L && R <= r[k]){
f[i][j] = (f[i][j] + f[i-1][k-1]*Choose(R-L+1, j-k+1)%mod)%mod;
}else break;
}
}
}
ll ans = f[num-1][n];
ans = ans*qm(fm, mod-2)%mod;
//assert(ans > 0);
cout<<ans<<endl;
}
/*
2
1 2
3 4
*/