文章目录
D - Fix a Tree
题意:
给出
n
n
n个结点的父亲,问至少修改多少个结点的父亲,能使整张图变成一棵树(根的父亲为自己),要求输出任一方案
思路:并查集
设定一个
a
n
s
ans
ans数组,
c
n
t
cnt
cnt表示需要修改的个数,
r
o
o
t
root
root表示最终的根节点,
a
n
s
i
=
−
1
ans_i=-1
ansi=−1表示原来父亲就是自己的节点,
a
n
s
i
=
−
2
ans_i=-2
ansi=−2表示在连接
i
,
a
i
i,a_i
i,ai时会产生环,
a
n
s
i
=
a
i
ans_i=a_i
ansi=ai表示已经成功的点,如果有
a
n
s
i
=
−
1
ans_i=-1
ansi=−1的点,就找一个点作为
r
o
o
t
root
root,如果没有就从
a
n
s
i
=
−
2
ans_i=-2
ansi=−2的点中选出一个作为
r
o
o
t
root
root,之后将
a
n
s
i
=
−
1
,
−
2
ans_i=-1,-2
ansi=−1,−2的点的根节点全部设为
r
o
o
t
root
root
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e5+10;
const int mod=1e9+7;
int n;
int a[N],p[N];
int ans[N];
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
bool merge(int a,int b)
{
int pa=find(a),pb=find(b);
if(pa==pb) return 0;
p[pa]=pb;
return 1;
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n;
rep(i,1,n)
{
p[i]=i;
cin>>a[i];
}
rep(i,1,n)
{
if(i==a[i]) ans[i]=-1;
else
{
if(merge(i,a[i])) ans[i]=a[i];
else ans[i]=-2;
}
}
int root=-1;
rep(i,1,n)
{
if(ans[i]==-1)
{
root=i;
ans[i]=i;
break;
}
}
int cnt=0;
if(root==-1)
{
rep(i,1,n)
{
if(ans[i]==-2)
{
root=i;
ans[i]=i;
cnt++;
break;
}
}
}
rep(i,1,n)
{
if(ans[i]<0)
{
if(i!=root)
{
ans[i]=root;
cnt++;
}
}
}
cout<<cnt<<endl;
rep(i,1,n) cout<<ans[i]<<' ';
return 0;
}
F - They Are Everywhere
题意:求一个最短区间可以包括所有字符,求最短区间长度
思路:尺取法
先取左端点
l
=
0
l=0
l=0,右端点
r
=
0
r=0
r=0,然后依次往右取,如果区间已经包括了所有字符,就右移
l
l
l,再右移
r
r
r使区间包括所有字符,取区间长度的最小值
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+10;
const int mod=1e9+7;
int n;
string s;
set<char> S;
map<char,int> cnt;
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>s;
for(auto c:s) S.insert(c);
int maxv=SZ(S);
int l=0,r=0;
int ans=2e9;
while(l<n)
{
while(r<n&&SZ(cnt)<maxv)
{
cnt[s[r]]++;
r++;
}
if(SZ(cnt)!=maxv) break; //如果右移r已经找不到覆盖全字符的区间了,那么右移l就更不可能了,直接break
ans=min(ans,r-l);
cnt[s[l]]--;
if(cnt[s[l]]==0) cnt.erase(s[l]);
l++;
}
cout<<ans;
return 0;
}
I - The Values You Can Make
题意:一个人要用最多 n n n个硬币凑出 k k k元的价值,求用于凑成k元的硬币重新组合后,可以得到哪些价值
思路:dp
状态表示:
d
p
i
j
k
dp_{{i}{j}{k}}
dpijk表示从
1
∼
i
1\sim i
1∼i选,当前所有数的和为
j
j
j,能否组成和为
p
p
p的子集
属性: b o o l bool bool
状态转移:
如果不使用第
i
i
i个数,
d
p
i
j
k
∣
=
d
p
i
−
1
,
j
−
w
i
,
p
−
w
i
dp_{ijk}\mid=dp_{i-1,j-w_i,p-w_i}
dpijk∣=dpi−1,j−wi,p−wi
如果使用第
i
i
i个数但不加入集合,
d
p
i
j
p
∣
=
d
p
i
−
1
,
j
−
w
i
,
p
dp_{ijp}\mid=dp_{i-1,j-w_i,p}
dpijp∣=dpi−1,j−wi,p
如果使用第
i
i
i个数加入集合,
d
p
i
,
j
,
k
∣
=
d
p
i
−
1
,
j
−
w
i
,
p
−
w
i
dp_{i,j,k}\mid=dp_{i-1,j-w_i,p-w_i}
dpi,j,k∣=dpi−1,j−wi,p−wi
答案: a n s + = d p n , k , ( 0 ∼ k ) ans+=dp_{n,k,(0\sim k)} ans+=dpn,k,(0∼k)
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=510;
const int mod=1e9+7;
int n,k;
int w[N];
bool dp[N][N][N];
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
dp[0][0][0]=1;
cin>>n>>k;
rep(i,1,n) cin>>w[i];
rep(i,1,n)
rep(j,0,k)
rep(p,0,j)
{
dp[i][j][p]|=dp[i-1][j][p];
if(j>=w[i]) dp[i][j][p]|=dp[i-1][j-w[i]][p];
if(j>=w[i]&&p>=w[i]) dp[i][j][p]|=dp[i-1][j-w[i]][p-w[i]];
}
int cnt=0;
rep(i,0,k) if(dp[n][k][i]) cnt++;
cout<<cnt<<endl;
rep(i,0,k) if(dp[n][k][i]) cout<<i<<' ';
return 0;
}
H - Mike and Chocolate Thieves
题意:
a
1
,
a
2
,
a
3
,
a
4
a_1,a_2,a_3,a_4
a1,a2,a3,a4组成一个等比数列,他们的组合方式有
m
(
m
<
=
1
e
15
)
m(m<=1e^{15})
m(m<=1e15)种,问构成的
a
4
a_4
a4的最大值最小是多少?
思路:二分
看到数据很大,和最大值最小所以想到二分,二分
a
4
a_4
a4的值,判断构造的种数是否大于
m
m
m,设
a
1
∗
k
3
=
a
4
a_1*k^3=a_4
a1∗k3=a4,则对
c
n
t
cnt
cnt的贡献为
d
=
⌊
a
4
k
3
⌋
d=\lfloor \frac{a_4}{k^3} \rfloor
d=⌊k3a4⌋,(如果
d
=
5
d=5
d=5,那么
1
,
2
,
3
,
4
,
5
1,2,3,4,5
1,2,3,4,5都可以作为
a
1
a_1
a1,对
c
n
t
cnt
cnt的贡献为
5
5
5),如果
c
n
t
=
m
cnt=m
cnt=m,就更新答案,否则输出
−
1
-1
−1
枚举时如果
(
k
3
>
x
)
(k^3>x)
(k3>x)直接break,因为
a
1
a_1
a1从
1
1
1开始,如果
c
n
t
>
=
m
cnt>=m
cnt>=m 直接返回
c
n
t
cnt
cnt
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e6+10;
const int mod=1e9+7;
int m;
int get(int x)
{
int cnt=0;
for(int i=2;;i++)
{
if(i*i*i>x||cnt>m) break;
cnt+=x/(i*i*i);
}
return cnt;
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>m;
int l=0,r=1e18;
int ans=-1;
while(l<r)
{
int mid=l+r>>1;
//cout<<l<<' '<<r<<endl;
int t=get(mid);
if(t>=m)
{
r=mid;
if(t==m) ans=r;
}
else l=mid+1;
}
cout<<ans;
return 0;
}
K - Alyona and Strings
题意:
给定两个字符串
s
,
t
s,t
s,t,在第一个串中找k个连续的子串(不相交),并且这些字串在第二个字符串中均出现且顺序相同,问这些字串最大的长度和。
思路:dp
状态表示:
d
p
i
,
j
,
k
,
0
/
1
dp_{i,j,k,0/1}
dpi,j,k,0/1表示匹配到了
s
i
s_i
si和
t
j
t_j
tj,并且已经匹配了
k
k
k段,1表示当前第
k
k
k段还会继续延伸,
0
0
0表示不再延伸,的配对长度
属性: m a x max max
状态计算:
如果
s
i
=
t
j
s_i=t_j
si=tj 则
d
p
i
j
k
1
=
m
a
x
(
d
p
i
−
1
,
j
−
1
,
k
−
1
,
0
+
1
,
d
p
i
−
1
,
j
−
1
,
k
,
1
+
1
)
\space dp_{ijk1}=max(dp_{i-1,j-1,k-1,0}+1,dp_{i-1,j-1,k,1}+1)
dpijk1=max(dpi−1,j−1,k−1,0+1,dpi−1,j−1,k,1+1)
d
p
i
,
j
,
k
,
0
=
m
a
x
(
d
p
i
,
j
,
k
,
1
,
d
p
i
,
j
−
1
,
k
,
0
,
d
p
i
−
1
,
j
,
k
,
0
,
d
p
i
−
1
,
j
−
1
,
k
,
0
)
dp_{i,j,k,0}=max(dp_{i,j,k,1},dp_{i,j-1,k,0},dp_{i-1,j,k,0},dp_{i-1,j-1,k,0})
dpi,j,k,0=max(dpi,j,k,1,dpi,j−1,k,0,dpi−1,j,k,0,dpi−1,j−1,k,0)
答案:
a
n
s
=
m
a
x
(
d
p
n
,
m
,
k
,
0
,
d
p
n
,
m
,
k
,
1
ans=max(dp_{n,m,k,0},dp_{n,m,k,1}
ans=max(dpn,m,k,0,dpn,m,k,1)
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e3+10;
const int mod=1e9+7;
int n,m,kk;
char s[N],t[N];
int dp[N][N][11][2];
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m>>kk;
cin>>s+1>>t+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=kk;k++)
{
if(s[i]==t[j]) dp[i][j][k][1]=max(dp[i-1][j-1][k-1][0]+1,dp[i-1][j-1][k][1]+1);
dp[i][j][k][0]=max(max(dp[i][j][k][1],dp[i-1][j-1][k][0]),max(dp[i-1][j][k][0],dp[i][j-1][k][0]));
}
cout<<max(dp[n][m][kk][0],dp[n][m][kk][1])<<endl;
return 0;
}