20级CUSTACM 2021年暑期训练五


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 1i选,当前所有数的和为 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=dpi1,jwi,pwi
如果使用第 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=dpi1,jwi,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=dpi1,jwi,pwi

答案: a n s + = d p n , k , ( 0 ∼ k ) ans+=dp_{n,k,(0\sim k)} ans+=dpn,k,(0k)

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 a1k3=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(dpi1,j1,k1,0+1,dpi1,j1,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,j1,k,0,dpi1,j,k,0,dpi1,j1,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值