Codeforces Round 898 (Div. 4) (E~H)

1873E - Building an Aquarium 

        题意:给定一个数组a和一个整数x,进行无限次操作,每次操作可以对数组a中任意一个数进行加法,加数的总和不超过x。求整个数组最小值的最大值。

        思路:直接二分找答案。

        

// Problem: E. Building an Aquarium
// Contest: Codeforces - Codeforces Round 898 (Div. 4)
// URL: https://codeforces.com/contest/1873/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N=2e05+10;
const LL mod=1e09+7;
typedef pairpl;
priority_queue, greater >t;
priority_queue q;
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int h[N];
int x;
int n;
bool check(int th){
	int tot = x;
	for(int i = 0 ; i < n ; i ++){
		int t = th - min(h[i] , th);
		tot -= t;
		if(tot < 0){
			return false;
		}
	}
	return true;
}
void solve() 
{
	cin>>n>>x;
	for(int i = 0 ; i < n ; i ++){
		cin>>h[i];
	}
	long long l = 0 , r = 2e9;
	while(l < r){
		int mid = (l + r)/2 + 1; 
		if(check(mid)){
			l = mid;
		}
		else{
			r = mid - 1;
		}
	}
	cout<>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

 1873F - Money Trees 

        题意:现有n棵果树和一个整数k,其中每棵树的高度为eq?h_%7Bi%7D , 每棵树上有eq?a_%7Bi%7D 个果子。现在规则如下:起初选中第 i 棵树,摘走全部果子,接下来若下一棵树的高度能够整除当前树的高度,则能且只能摘走树上所有果实,以此类推。但是,需要注意总共摘的果子不能超过k。求最多能够连续摘多少棵树上的果子。

        思路:用双指针,其中 l 表示最起始的那棵树,r表示最终的树,r 依次递增,看l是否能满足条件,记录( r - l + 1 ) 的最大值。注意果子不超过k即可。

        

// Problem: F. Money Trees
// Contest: Codeforces - Codeforces Round 898 (Div. 4)
// URL: https://codeforces.com/contest/1873/problem/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N=2e05+10;
const LL mod=1e09+7;
typedef pairpl;
priority_queue, greater >t;
priority_queue q;
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int h[N] , a[N] , k , n;
void solve() 
{
	cin>>n>>k;
	for(int i = 1 ; i <= n ; i ++){
		cin>>a[i];
	}
	for(int i = 1 ; i <= n  ; i ++)
		cin>>h[i];
	int l = 1;
	int ans = a[1] > k ? 0 : 1;
	int tot = a[1];
	for(int r = 2 ; r <= n ; r ++){
		if(h[r - 1] % h[r]== 0){
			tot += a[r];
		}
		else{
			l = r;
			tot = a[r];
		}
		while(tot > k){
			tot -= a[l];
			l ++;
		}
		ans = max(ans , r - l + 1);
	}	
	cout<>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

 1873G - ABBC or BACB

        题意:现有一个只含有“A”“B”的字符串,起初拥有0枚硬币,接下来能够进行如下操作:

1、选择连续的“AB” , 将其改为“BC” ,然后获得一枚硬币。

2、选择连续的“BA” , 将其改为 “CB” ,然后获得一枚硬币。

求能够获得硬币的最大值。

        思路:最值问题直接考虑 dp[n][3] ,共有三种状态 : 1、当前字母不发生改变。2、当前位置的"B"通过情况1变成了“C” 。 3 、当前位置的"A” 通过情况2变成了“B”。

        三种状态转移:1、若不改变,则直接从前一位的三种状态取最值转移。2、如果当前位置是“B”且前一位是“A”,需要注意的是:例如“AAB”的情况,变成了“ABC”之后,上一位也能变成“BBC”,这样就能多一枚硬币,所以转移的时候需要一直往前找连续的A直到发现“B”或者起点为止。3、当前位置是“A”时, 如果前一位本来是"B" , 直接从dp[i - 1][0]转移过来 , 或者前一位通过操作2变成了"B",从dp[i - 1][2]转移过来。最终输出dp[n + 1][0]即可。

        

// Problem: G. ABBC or BACB
// Contest: Codeforces - Codeforces Round 898 (Div. 4)
// URL: https://codeforces.com/contest/1873/problem/G
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N=1e05+10;
const LL mod=1e09+7;
typedef pairpl;
priority_queue, greater >t;
priority_queue q;
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
void solve() 
{
	string s;
	cin>>s;
	int n = s.size();
	int dp[n + 5][3];//不变 AB-BC BA-CB
	memset(dp , -0x3f3f , sizeof dp);
	dp[0][0] = 0;
	for(int i = 1 ; i <= n ; i ++){
		dp[i][0] = max(dp[i - 1][0] , max(dp[i - 1][1] , dp[i - 1][2]));
		if(i == n)
			break;
		if(s[i] == 'B' && s[i - 1] == 'A'){
			int len = 0;
			for(int l = i - 1 ; l >= 0 ; l --){
				if(s[l] == 'A') len++;
				else break;
			}
			dp[i][1] = max(dp[i][1] , dp[i - len][0] + len);
		}
		if(s[i] == 'A'){
			if(s[i - 1] == 'B')
				dp[i][2] = max(dp[i][2] , dp[i - 1][0] + 1);
			dp[i][2] = max(dp[i][2] , dp[i - 1][2] + 1);
		}
	}		
	cout<>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

 1873H - Mad City 

        题意:有一个n个点,n条边的图。其中 Valeriu 和 Marcel 在玩游戏,Valeriu 需要逃脱 Marcel的追踪 ,每回合当中,他们能够选择走到相邻的点或者停留在原点。因为 Valeriu 十分了解 Marcel ,所以他能够知道Marcel在本回合当中会往哪里走,从而做出应对。判断 Valeriu 能否 不被 Marcel抓住。

        思路:n个点n条边,可以得出这是一颗基环树,若 Valeriu 起初在环上面,那么由于他可以预知对方的行动,他就能够永远的不被 Marcel 抓住。现考虑不在环上的情况: Valeriu 需要走到环上面,这样才能确保自己不会被抓住。若不走到环上面,那么迟早会走到树的叶子结点,那么此时将会无路可走,最终被抓住。因此需要判断的是:Valeriu 到环上所有点的距离和 Marcel 到环上所有点的距离,若其中有一个点Valeriu 小于 Marcel ,那么代表 Valeriu 能够提前到达环上面,这样就能永远不被抓住了。

        找环可以用拓扑排序来做,时间复杂度O(n) , 而距离可以直接BFS一遍做,时间复杂度也是O(n),整体时间复杂度为O(3 * n)。

// Problem: H. Mad City
// Contest: Codeforces - Codeforces Round 898 (Div. 4)
// URL: https://codeforces.com/contest/1873/problem/H
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N=1e05+10;
const LL mod=1e09+7;
typedef pairpl;
priority_queue, greater >t;
priority_queue q;
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
void solve() 
{
	int n , a , b;
	cin>>n>>a>>b;
	vectortr[n + 5];
	int in[n + 5];
	memset(in , 0 ,sizeof in);
	int x , y;
	for(int i = 1 ; i <= n ; i++){
		cin>> x >> y;
		tr[x].pb(y);
		tr[y].pb(x);
		in[x] ++;
		in[y] ++;
	}	
	unordered_map ishuan;
	queueq;
	for(int i = 1 ; i <= n ; i ++){
		if(in[i] == 1){
			q.push(i);
		}
	}
	vectorpos;
	while(!q.empty()){
		int x = q.front();
		q.pop();
		for(auto it : tr[x]){
			in[it]--;
			if(in[it] == 1){
				q.push(it);
			}
		}
	}
	for(int i = 1 ; i <= n ; i ++){
		if(in[i] > 1){
			ishuan[i] = 1;
			pos.pb(i);
		}
	}
	if(a == b){
		cout<<"NO\n";
	}
	else if(ishuan[a] && ishuan[b]){
		cout<<"YES\n";
	}
	else if(ishuan[b]){
		cout<<"YES\n";
	}
	else{
		int disa[n + 5];
		disa[a] = 0;
		queueq;
		int vis[n + 5];
		memset(vis , 0 , sizeof vis);
		q.push(a);
		while(!q.empty()){
			int x = q.front();
			vis[x] = 1;
			q.pop();
			for(auto it : tr[x]){
				if(!vis[it]){
					disa[it] = disa[x] + 1;
					vis[it] = 1;
					q.push(it);
				}
			}
		}
		int disb[n + 5];
		disb[b] = 0;
		memset(vis , 0 , sizeof vis);
		q.push(b);
		while(!q.empty()){
			int x = q.front();
			vis[x] = 1;
			q.pop();
			for(auto it : tr[x]){
				if(!vis[it]){
					disb[it] = disb[x] + 1;
					vis[it] = 1;
					q.push(it);
				}
			}
		}
		int len = pos.size();
		int f = 0;
		for(int i = 0 ; i < len ; i ++){
			if(disb[pos[i]] < disa[pos[i]])
				f = 1;
		}
		if(f){
			cout<<"YES\n";
		}
		else
			cout<<"NO\n";
	}
}             
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

        

        

你可能感兴趣的:(算法,数据结构,c++)