cf 思维专题2

B. Napoleon Cake
因为每个层对应的 a[i] 都是向下渗透,所以我们选择从后往前扫一遍。
开始设置一个cnt表示当前还可以渗透的多少层。
每次维护一下最大的

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 5;
ll a[N], ans[N];
ll n, m, T, x;
signed main()
 {
    cin >> T;
    while(T--) 
    {
        cin >> n;
        for(int i = 1;i <= n;i++)
        {
            scanf("%lld", &a[i]);
            ans[i] = 0;
        }
        ll cnt = 0;
        for(int i = n; i >= 1; --i)
        {
        	cnt = max(cnt, a[i]);
        	ans[i] = (cnt > 0);
        	--cnt;
		}
		for(int i = 1; i <= n; ++i)
		printf("%lld%c", ans[i] > 0 ? 1ll : 0ll, i == n ? '\n' : ' ');
    }
    return 0;
}

B. AGAGA XOOORRR
位运算:异或
题意:选两个相邻的数,用异或值取代两个数,数组长度 -1,要求最后剩下的数都相等,至少两个。
分析:最后剩下 k 个相等的数, sum为所有数异或值

  1. sum = 0, 一定留下 k 个相等的数,k 为偶数

  2. sum != 0 ,如果存在,那么 k 一定为奇数

  3. 在这里插入图片描述

  4. 被 sum 分割的区间异或值为0,如果区间个数 >= 3, 就满足条件

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005;
int a[N];
int t, n;
int main() 
{
    cin >> t;
    while (t--) 
	{
        cin >> n;
        int sum = 0;
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]), sum ^= a[i];
        if (sum == 0) 
		{
            printf("YES\n");
            continue;
        }
        int cnt = 0;
        for (int i = 1, j; i <= n; i = j + 1) 
		{
            j = i;
            int now = a[i];
            while (j < n && now != sum) ++j, now ^= a[j];
            
            if (now == sum)  ++cnt;       
        }
        if (cnt >= 3) printf("YES\n");
         else printf("NO\n");   
    }
    return 0;
}

B. Binary Removals
因为我们要在序列前边删除多余的1,如果出现 11,我们就试图删除在此后边多余的并且不连续的0,如果在出现 11 之后 出现了 00,那么就是NO,否则就是 YES

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005;
int a[N];
int t, n;
string s;
int f[N];
int main() 
{
    cin >> t;
    while (t--) 
	{
        cin >> s;
        bool f1 = 0, f0 = 0, f = 1;
        for(int i = 1; i < s.length(); ++i)
        {
        	if(s[i] == s[i-1] && s[i] ==  '1') f1 = 1;
        	else if(s[i] == s[i-1] && s[i] == '0' && f1) f0 = 1;
        	if(f1 && f0)
			{
        		f = 0;
        		break;
			}
		}
		if(f) cout << "YES\n";
		else cout << "NO\n";
    }
    return 0;
}

B. Replace and Keep Sorted
注意 所找的数组只能有一个数和原数组不同
每次只需要在所选的数组中 替换一个数即可
对于每个数求出可替换的数的数目
由于查询次数比较多,所有想到了前缀和。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 9;
int a[N];
int t, n, q, k;
int l, r;
string s;
int f[N];
int main() 
{
    cin >> n >> q >> k;
    for(int i = 1; i <= n; ++i)
    	scanf("%d", &a[i]);
    if(n == 1)
    {
    	while(q--)
    	{
    		cout << k-1 << endl;
		}
		return 0;
	}
    for(int i = 1; i <= n; ++i)
    {
    	if(i == 1)
    		f[1] = a[2] - 1 - 1;	
		else if(i == n)
			f[n] = k - a[n-1] - 1;
		else f[i] = a[i+1] - a[i-1] - 2;
		f[i] += f[i-1];
	}
	for(int i = 1; i <= n; ++i)
		cout << f[i] <<  " ";cout << endl;
    while(q--)
    {
    	scanf("%d %d", &l, &r);
    	if(l == r)
		{
    		cout << k - 1 << endl;continue;
		}
    	int ans = f[r-1] - f[l]     +   a[l+1] - 1 - 1 + k - a[r-1] - 1;
    	//   替换从 a[l+1] --->a[r-1]     替换 a[l] 	 	替换 a[r] 
    	cout << ans << endl;
	}
    return 0;
}

C. Maximum width
题意:就是在 s 串找出 t 串(保证一定有),然后找出求 所有找出的 t 串中,两个字符下标差值最大值
贪心做的,从左向右扫,存每个字符下标的最左端,然后从右向左扫,存每个字符下标的最右端,然后求最大的一个下标差即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
int n, m;
string s, t;
vector <int> v1, v2;
int Max;
int main()
{
	cin >> n >> m;
	cin >> s >> t;
	for(int i = 0, j = 0; i < n && j < m; ++i)
	{
		if(s[i] == t[j]) v1.push_back(i), ++j;
	}
	for(int i = n-1, j=m-1; i >= 0 && j >= 0; --i)
	{
		if(s[i] == t[j]) v2.push_back(i), --j;
	}
	reverse(v2.begin(),v2.end());
	for(int i = 1; i < v1.size(); ++i)
	{
		Max = max(Max,v2[i] - v1[i-1]);
		//cout << v1[i] << " " << v2[i] << endl;
	}
	cout << Max << endl;
	return 0;
}

C. Long Jumps
数据比较大, 如果我们暴力肯定会超时
考虑到每次我们都是从前往后扫,并且结束都是下标大于n的时候结束
因为当前的下标是小于n的,它往后的路径是确定的,所以我们从后往前扫,存后边的路径,扫到前边时可以直接返回答案,就是类似于记忆化搜索。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;

int t, n;
long long a[N];
int f[N];
int main()
{
	cin >> t;
	while(t--)
	{
		memset(f,0,sizeof(f));
		cin >> n;
		for(int i = 1; i <= n; ++i)	scanf("%lld", &a[i]);
		long long Max = 0;
		for(long long i = n; i >= 1; --i)
		{
			// 当前为 i
			long long k = i + a[i];// 下一个 
			while(k <= n) 
			{
				if(f[k])// 类似于记忆化搜索 
				{
					k += f[k];break;
				}
				k += a[k];
			}
			Max = max(k-i, Max);
			f[i] = k - i;
		}
		cout << Max << endl;
	}
	return 0;
}

D. Even-Odd Game
贪心
每次选取当前最大的数,要么加上给自己,要么移除也不给对方加分。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;

int t, n;
long long a[N];
bool cmp(long long x, long long y)
{
	return x > y;
 } 
//int f[N];
int main()
{
	cin >> t;
	while(t--)
	{
		cin >> n;
		for(int i = 1; i <= n; ++i)	scanf("%lld", &a[i]);
		sort(a+1,a+1+n,cmp);
		long long sum1 = 0, sum2 = 0;
		for(long long i = 1; i <= n; ++i)
		{
			if(i & 1 && a[i] % 2 == 0) sum1 += a[i];
			if(i % 2 == 0 && a[i] & 1) sum2 += a[i];
		}
		if(sum1 > sum2) cout << "Alice\n";
		else if(sum1 < sum2) cout << "Bob\n";
		else cout << "Tie\n";
	}
	return 0;
}

B. Strange List

题意:给你一个数组和x,你的初始分数为0。从头开始遍历数组,对于当前的元素,不能整除x时就结束,输出分数;可以整除x就将分数增加当前数字大小,并且在数组末尾添加x个大小为a[i]除以x的元素。

如果按照题意来模拟数组肯定会爆掉…其实我们不需要不断更新数组a,a[i] = a[i] / x,但只要a[i] % x == 0 那么就可以贡献初始的a[i]一次。
这里要注意初始的分数其实是给你的数组元素和,b[i]记录初始a[i]

由于我们会不断向数组里增添数据,终止的条件是恰好遇到数组中某个不能整除x的数,所以不能直接对一个数据进行分解。

#include <bits/stdc++.h>
using namespace std;
#define qc std::ios::sync_with_stdio(0);
int a[100001];
int b[100001];

int main() {
	qc;
	cin.tie(0);
	int t;
	cin >> t;
	while (t--) {
		int n, k;
		long long ans = 0;
		cin >> n >> k;
		for (int i = 0; i < n; i++) {
			cin >> a[i];
			b[i] = a[i];
			ans += a[i];
		}
		int flag = 0;
		while (1) {
			for (int i = 0; i < n; i++) {
				if (a[i] % k != 0) {
					flag = 1;
					break;
				} else {
					a[i] = a[i] / k;
					ans += b[i];
				}
			}
			if (flag == 1)
				break;
		}
		cout << ans << endl;
	}
}



C1. Pokémon Army (easy version)
wa了一个小时,没改出来
网上一搜这个代码居然a了,我直接震惊,擦
这可能就是贪心解法?

#include<bits/stdc++.h>
#define ll long long
#define ios ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=3e5+5,INF=0x3f3f3f3f;
ll a[maxn];
int main()
{
    ios;
    int t;
    cin >> t;
    while(t--)
    {
        int n, q;
        cin >> n >> q;
        for(int i = 1; i <= n; i++)
            cin >> a[i];
        a[n + 1] = 0;
        ll ans = 0;
        for(int i = 1; i <= n; ++i)
        {
        	if(a[i + 1] < a[i])
        		ans += a[i] - a[i + 1];
		}
        cout << ans << endl;
    }
    return 0;
}

dp解法
粘一下博客叭

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 9;
ll dp[N][2];
ll a[N];
int main()
{
	int t, n, q;
	cin >> t;
	while(t--)
	{
		memset(dp,0,sizeof(dp));
		cin >> n >> q;
		a[n+1] = 0;
		for(int i = 1; i <= n; ++i)	scanf("%lld", &a[i]);
		for(int i = 1; i <= n; ++i)
		{
			dp[i][0] = max(dp[i-1][0], dp[i-1][1] - a[i]);
			dp[i][1] = max(dp[i-1][1], dp[i-1][0] + a[i]);
		}
		cout << max(dp[n][0], dp[n][1]) << endl;
	}
	return 0;
}

B. Nastia and a Good Array
类似构造通解的题

#include<bits/stdc++.h>
using namespace std;
const int maxn=100000+50;
int n,m,ans,T,a[maxn];
int main()
{
    scanf("%d",&T);
    for (int oo=1; oo<=T; ++oo)
    {
        scanf("%d",&n);
        for (int i=1; i<=n; ++i) scanf("%d",&a[i]);
        printf("%d\n",n/2);
        for (int i=1; i+1<=n; i+=2)
        {
            int t=min(a[i],a[i+1]);
            if (t>=998244353)
            {
                if (t==a[i-1]) ++t;
                printf("%d %d %d %d\n",i,i+1,t,t+1); a[i+1]=t+1;
            }
            else
            {
                printf("%d %d %d %d\n",i,i+1,t,998244353);
                a[i+1]=998244353;
            }
        }
    }
    return 0;
}

A. Omkar and Bad Story
假如只有正数,那么每次相减取绝对值只会得到一个不大于这两个数较大的数的数,如果有负数,那么它和任意一个数作差都会让需要添加的数越来越大,不可能存在每个数都在数组里。

#include<bits/stdc++.h>
using namespace std;
int t, n;
int a;
void solve()
{
	bool f = 1;
	cin >> n;
	for(int i = 1; i <= n; ++i)	{
		cin >> a;if(a < 0) f = 0;
 	}
 	if(!f) cout << "NO\n";
 	else 
 	{
 		cout << "YES\n";
		cout << "101\n";
		for(int i = 0 ; i <= 100; ++i) cout << i <<" ";cout << endl;	
	}
	
}
int main()
{
	cin >> t;
	while(t--)
		solve();
	return 0;
}

B. Pleasant Pairs
解法一:用结构体存一下所有元素的值和下标,然后sort一下值,从小到大
两层for循环遍历,元素乘积 > 2*n 就break掉。
(这个思路比较简单,好理解
解法二:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 9;
ll t = 1, n, m, k;
ll pos[N];
int main()
{
	int t;
	cin >> t;
	while(t--)
	{
		scanf("%lld", &n);
		for(int i = 1; i <= 2*n; ++i) pos[i] = 0;
		for(ll i = 1, c; i <= n; ++i) scanf("%lld", &c), pos[c] = i;
		ll ans = 0;
		for(int i = 3; i <= 2*n-1; ++i)// 遍历 i+j的和
		{
			for(int j = 1; 1ll*j*j < i; ++j)// 遍历加和的因子
			{
				if(i % j != 0) continue;// i % j 必须等于0
				int k = i / j;// 也就是 k 必须为整数,因为它是因子
				if(pos[j] && pos[k] && pos[j] + pos[k] == i) ++ans;
				// 这两个因子存在,并且下标加和等于 i 就++ans
				// 查询这两个因子中有没有输入的时间复杂度仅为 O(1)
			}
		}
		cout << ans << endl;
	}
	return 0;
}

C. Great Graphs
看题目意思好像是,把1-n走一遍,加上1-n的所有正边权(排序完之后的第n个边),然后减去任意两边之间的边权值,就是答案

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 1e5 + 9;
typedef long long ll;
ll t = 1, n, m, k;
ll a[N];
int main()
{
	cin >> t;
	while(t--)
	{
		cin >> n;
		ll sum = 0;
		for(int i = 1; i <= n; ++i)	scanf("%lld", &a[i]), sum += a[i];
		sort(a+1,a+1+n);
		ll ans = a[n];
		ans -= sum;// 先减去所有边到 1 的边权值
		for(int i = 1; i < n; ++i)
		{
			sum -= (a[i+1] - a[i]) * (n - i);// sum里面 i ---> i + 1 的边加了 n - i 次 
			ans -= sum;//然后减去所有边到 i+1 的边权值
		}
		cout << ans << endl;
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值