• Codeforces Round #700 (Div. 2) A~D题解


    本场链接:Codeforces Round #700 (Div. 2)

    A. Yet Another String Game

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 55;
    char s[N];
    
    int main() 
    {
    	int T;scanf("%d",&T);
    	while(T--)
    	{
    		scanf("%s",s + 1);int n = strlen(s + 1);
    		forn(i,1,n)
    		{
    			if(i & 1)	s[i] = (s[i] == 'a' ? 'b' : 'a');
    			else 		s[i] = (s[i] == 'z' ? 'y' : 'z');
    		}
    		printf("%s
    ",s + 1);
    	}
    	return 0;
    }
    

    B. The Great Hero

    思路

    这个题唯一一点比较特殊的点在于:如果人无论如何都会死,事实上也不一定就是NO,因为可能出现"同归于尽"的情况.这个时候可以讨论一下:首先第一种就是所有人直接一个一个敲死都不会让人死亡,这种肯定直接是YES,那么剩下的情况就是人无论如何都会死,可以枚举一下每个怪物作为最后一个去击杀的时候是否会出现"同归于尽"的情况,具体来说可以先把所有怪物完全击杀的代价算出来做和,然后枚举每个怪物作为最后一个的时候,当前剩余的血量和怪物的代价算一下是否会同归于尽就可以了.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 1e5+7;
    
    struct Node
    {
    	ll a,b,cost;
    	bool operator<(const Node& r)	const
    	{
    		return cost < r.cost;
    	}
    }a[N];
    
    int main() 
    {
    	ios::sync_with_stdio(0);cin.tie(0);
    	int T;cin >> T;
    	while(T--)
    	{
    		ll A,B,n;cin >> A >> B >> n;
    		forn(i,1,n)	cin >> a[i].a;
    		forn(i,1,n)	cin >> a[i].b;
    		
    		forn(i,1,n)	a[i].cost = (a[i].b + A - 1) / A * a[i].a;
    		ll sum = 0;forn(i,1,n)	sum += a[i].cost;
    		if(B >= sum)
    		{
    			cout << "YES
    ";
    			continue;
    		}
    		
    		bool ok = 0;
    		forn(i,1,n)
    		{
    			if(B <= sum - a[i].cost)	continue;
    			ll ts = (a[i].b + A - 1) / A;
    			// cout << ts << " " << B - sum + a[i].cost << endl;
    			if((ts - 1) * a[i].a <= B - sum + a[i].cost)
    			{
    				ok = 1;
    				break;
    			}
    		}
    		if(ok)	cout << "YES
    ";
    		else cout << "NO
    ";
    	}
    	return 0;
    }
    

    C. Searching Local Minimum

    思路

    有一个更巧妙的思考方式:我们事实上可以把这个问题看做是在维护一个区间([l,r])并且始终保证(l)端点的值小于他左边一个位置的值,(r)端点的值小于他右边一个位置的值.接下来如果我们能找到一个收缩这个区间到一个点的办法,也就是说一定能保证(l=r)的话,自然就找到了答案,这个收缩区间的方法可以求整个区间的中点(mid),那么现在的问题就是看(l)(r)谁应该被移动到(mid),如果直接看(mid)和两个端点的值的关系肯定是没用的,不妨看(mid)(mid+1)的大小关系,剩下的做法就是顺理成章的了.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 1e5+7;
    int a[N];
    
    int main() 
    {
    	ios::sync_with_stdio(0);cin.tie(0);
    	int n;cin >> n;
    	if(n == 1)
    	{
    		cout << "? 1" << endl;
    		int x;cin >> x;
    		cout << "! " << x << endl;
    		return 0;
    	}
    	int l = 1,r = n;
    	while(l < r)
    	{
    		int mid = l + r >> 1;
    		if(!a[mid])
    		{
    			cout << "? " << mid << endl;
    			cin >> a[mid];
    		}
    		if(!a[mid + 1])
    		{
    			cout << "? " << mid + 1 << endl;
    			cin >> a[mid + 1];
    		}
    
    		if(a[mid] < a[mid + 1])	r = mid;
    		else l = mid + 1;
    	}
    	cout << "! " << l << endl;
    	return 0;
    }
    

    Painting the Array

    由于线段树dp做法可以过两个就放在一起了

    D1 思路

    假设我们现在在考虑(a_i)加入两个序列的影响,那么在(a_i)实际的加入产生影响之前,我们只知道(a_{i-1})一定是其中一个序列的末尾元素,另外有一个序列还不知道,对于这个题来说我们可以说只关注一个序列的末尾元素,所以其中一个序列的信息已经完整的知道了,另外一个不妨做一个dp,用(f[x])表示末尾为(x)的序列的答案最大值是多少.现在这个问题可以这样处理了:首先我们知道(a_{i-1})一定是其中一个序列的末尾,其次可以用dp的方式维护另外一个序列的各种情况下的答案.

    考虑(a_i)加入两个序列产生的影响:

    • 如果(a_i eq a_{i-1}),那么可以直接把(a_i)丢到(a_{i-1})那条序列的末尾,这个时候,另外一条序列里面所有答案都应该(+1).
    • 如果(i>1)的话,我们也可以把(a_i)放在另外一个序列的末尾,但是这个时候产生了一个问题:对于下一次来(a_{i+1})的时候,现在dp维护的值末尾的元素是(a_i),根据定义来说应该维护的是另一个序列才对.也就是说此时应该交换一下两个序列.这个时候应该让(f[a[i - 1]]),也就是末尾值是上一个值的答案的最大值,增加以(a_i)为末尾的另外一个序列会产生的影响.更详细的说,这里根据直觉来看的话应该是让(f[a[i]] = max{f[x] + [x eq a[i]]}),其中(x)是那条序列的末尾元素,值可以任取.符合直觉的是:让(a_i)变成新的末尾,直接更新(a_i)这个位置上的值,但是这里因为维护的对象要交换一次,所以事实上应该是修改(f[a[i - 1]] = max{f[x] + [x eq a[i]]}).
    • 然后我们可以发现粗暴维护dp的值肯定是不行的,但是他形式上的操作等同于区间加/区间取max/单点更新max.所以可以用一个线段树来维护值最后答案就是所有的最大值.

    D1代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 2e5+7;
    struct Node
    {
    	int l,r,v;
    	int add;
    }tr[N * 4];
    int a[N];
    
    void pushup(int u)
    {
    	tr[u].v = max(tr[u << 1].v,tr[u << 1 | 1].v);
    }
    
    void pushdown(int u)
    {
    	auto& s = tr[u];
    	auto& lf = tr[u << 1];
    	auto& rt = tr[u << 1 | 1];
    	if(s.add)
    	{
    		lf.add += s.add;
    		lf.v += s.add;
    		
    		rt.add += s.add;
    		rt.v += s.add;
    		
    		s.add = 0;
    	}
    }
    
    void build(int u,int l,int r)
    {
    	if(l == r)	tr[u] = {l,r,0,0};
    	else
    	{
    		int mid = l + r >> 1;
    		tr[u] = {l,r,0,0};
    		build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
    	}
    }
    
    void modify(int u,int l,int r,int v)
    {
    	if(tr[u].l >= l && tr[u].r <= r)
    	{
    		tr[u].v += v;
    		tr[u].add += v;
    		return ;
    	}
    	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);
    }
    
    void modify(int u,int p,int v)
    {
    	if(tr[u].l == p && p == tr[u].r)
    	{
    		tr[u].v = max(tr[u].v,v);
    		return ;
    	}
    	pushdown(u);
    	int mid = tr[u].l + tr[u].r >> 1;
    	if(p <= mid)	modify(u << 1,p,v);
    	if(p > mid)		modify(u << 1 | 1,p,v);
    	pushup(u);
    }
    
    int query(int u,int l,int r)
    {
    	if(tr[u].l >= l && tr[u].r <= r)	return tr[u].v;
    	pushdown(u);
    	int mid = tr[u].l + tr[u].r >> 1,res = 0;
    	if(l <= mid)	res = max(res,query(u << 1,l,r));
    	if(r > mid)		res = max(res,query(u << 1 | 1,l,r));
    	return res;
    }
    
    int main() 
    {
    	int n;scanf("%d",&n);
    	forn(i,1,n)	scanf("%d",&a[i]);
    	build(1,1,n);
    	
    	forn(i,1,n)
    	{
    		int rt = max({query(1,1,a[i] - 1) + 1,query(1,a[i] + 1,n) + 1,query(1,a[i],a[i])});
    		if(a[i] != a[i - 1])	modify(1,1,n,1);
    		if(i > 1)	modify(1,a[i - 1],rt);
    	}
    	
    	printf("%d",tr[1].v);
    	return 0;
    }
    

    D2

    有了上面的步骤,D2也是很自然的把max换成min就可以了.但是没这么直接,还有一点小问题:首先D1里面讨论的时候事实上忽略了初值的问题,也就是(f[0]=0),这个在D1里面求最大值是不会有区别的,因为除了第一次不会有任何转移需要(0)位的元素,但是在D2里面求最小值是有可能的,而线段树的下标是从(1)开始的,所以我的代码里面把所有的位置全体往后挪动了一位,用(1)位的值表示(0)位.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 2e5+7,INF = 1e9;
    struct Node
    {
    	int l,r,v;
    	int add;
    }tr[N * 4];
    int a[N];
    
    void pushup(int u)
    {
    	tr[u].v = min(tr[u << 1].v,tr[u << 1 | 1].v);
    }
    
    void pushdown(int u)
    {
    	auto& s = tr[u];
    	auto& lf = tr[u << 1];
    	auto& rt = tr[u << 1 | 1];
    	if(s.add)
    	{
    		lf.add += s.add;
    		lf.v += s.add;
    		
    		rt.add += s.add;
    		rt.v += s.add;
    		
    		s.add = 0;
    	}
    }
    
    void build(int u,int l,int r)
    {
    	if(l == r)	tr[u] = {l,r,INF,0};
    	else
    	{
    		int mid = l + r >> 1;
    		tr[u] = {l,r,INF,0};
    		build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
    	}
    }
    
    void modify(int u,int l,int r,int v)
    {
    	if(tr[u].l >= l && tr[u].r <= r)
    	{
    		tr[u].v += v;
    		tr[u].add += v;
    		return ;
    	}
    	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);
    }
    
    void modify(int u,int p,int v)
    {
    	if(tr[u].l == p && p == tr[u].r)
    	{
    		tr[u].v = min(tr[u].v,v);
    		return ;
    	}
    	pushdown(u);
    	int mid = tr[u].l + tr[u].r >> 1;
    	if(p <= mid)	modify(u << 1,p,v);
    	if(p > mid)		modify(u << 1 | 1,p,v);
    	pushup(u);
    }
    
    int query(int u,int l,int r)
    {
    	if(tr[u].l >= l && tr[u].r <= r)	return tr[u].v;
    	pushdown(u);
    	int mid = tr[u].l + tr[u].r >> 1,res = INF;
    	if(l <= mid)	res = min(res,query(u << 1,l,r));
    	if(r > mid)		res = min(res,query(u << 1 | 1,l,r));
    	return res;
    }
    
    int main() 
    {
    	int n;scanf("%d",&n);
    	forn(i,1,n)	scanf("%d",&a[i]);
    	build(1,1,n + 1);
    	
    	modify(1,1,0);
    	forn(i,1,n)
    	{
    		int rt = min({query(1,1,a[i]) + 1,query(1,a[i] + 2,n + 1) + 1,query(1,a[i] + 1,a[i] + 1)});
    		if(a[i] != a[i - 1])	modify(1,1,n + 1,1);
    		if(i > 1)	modify(1,a[i - 1] + 1,rt);
    	}
    	
    	printf("%d",tr[1].v);
    	return 0;
    }
    
  • 相关阅读:
    LR--用栈实现移进--归约分析(demo)
    阿里云ECS服务器socket无法连接的问题
    select客户端模型封装——回调方式快速建立客户端
    select服务器端模型封装——回调方式快速建立服务端
    python实现的ocr接口
    汉字字典树
    linux下简易端口扫描器
    Linux下cs简单通讯(socket)
    POj 1321 棋盘问题 DFS 回溯
    HDU 1097 快速幂
  • 原文地址:https://www.cnblogs.com/HotPants/p/14389813.html
Copyright © 2020-2023  润新知