4.5Codeforces Round 905 (Div. 3)

1.C题
题意:几个数字,挑选其中几个进行+1.得到的数字的乘积可以整除k
注意题目条件:
k只能是2,3,4,5这几种情况
每个数字只能是1——10(不算大)(好像没啥用,因为可以自增超过10)
思路:
对于2,3,5要想整除,这些个数里必须要有一个数是可以整除2,3,5的。所以思路很简单,去造,找到一个“变成5的倍数”所需+1最少的就行
对于4,分为几种情况:
1、原数组有两个偶数,那一定可以整除4,
2、原数组只有一个偶数,那就是输出1,找个奇数+1,一次操作即可
3、原数组没有偶数,全是奇数,那么就必须和2,3,5一样手动造4了,找到一个“变成4的倍数”所需+1最少的就行,但是要特判1,1

#include<bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;



signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);
    int t = 1; cin >> t;
    while(t--) 
    {
    	int x,n,k;cin>>n>>k;
    	int red=0;
    	int ans=1e18;
		for(int i=0;i<n;i++)//输入数据
		{
			cin>>x;
			if(x%k==0)
			ans=0;
			ans=min(ans,(k-x%k));//手动造数
				
			if(x%2==0)//计算奇偶数量,对4的情况而做的
			red++;
		} 
		
		 if(ans==0)//如果整除了,直接输出0即可
		 {
		 	cout<<ans<<endl;
		 	continue;
		 }
		 
		 if(k==4)
		 {
		 	if(red==0)//没有偶数
		 	{//下面是对1,1的特判
              //1,1 这是需要造3的,但是2就可以了
             //3,3这是需要造1的
             //所以,防止1,1的情况,如果出现了3,就给他改成2.   4这个模块下不可能有3,一定都能转换成2
		 		cout<<min((int)2,ans)<<endl;
			 }
			 else if(red==1)
			 {
			 	cout<<1<<endl;
			 }
			 else
			 {
			 	cout<<0<<endl;
			 }
		 }
		 else
		 {
		 	cout<<ans<<endl;
		 }
	}
    return 0;
}

2、D题
题意:给你一堆线段,判断这一堆线段里有没有不重合的线段
思路:放缩思想之看边界:要看有没有,只需要看两个离得最远的两个线段。只要有不重合的,他俩一定是第一个。
实现:最左边的线段,看他的右端点。最右边的线段,看它的左端点。
涉及一个排序问题,把所有的左端点放入multiset即可。
注意:如果你只是想用排序,那就用multiset,用set的话,判重是非常消耗时间的,可能导致超时。

#include<bits/stdc++.h>
using namespace std;
#define int long long
using i64 = long long;

map<int,int> a;

signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);
    int t = 1; cin >> t;
	multiset <int> L,R;
    while(t--) 
    {
    	char c;cin>>c;
		int x,y;cin>>x>>y;
		if(c=='+')
		{
			L.insert(x);
			R.insert(y);
		}
		else
		{
			L.erase(L.find(x));
			R.erase(R.find(y));
		}
		if(!L.empty()&&*L.rbegin()>*R.begin())
		{
			cout<<"yes"<<endl;
		}
		else
		cout<<"no"<<endl;
		
	}
    return 0;
}

3、E题
题意:一串数字,每次操作是从里面选一个,使其乘2,问你至少需要多少次,这个数列单调不减
思路:分为两种情况:
1、321(连续往下低):2要乘2,1肯定得至少先跟着走一次。不管有没有第二次,这次必须得有,所以这种连续低的模型就可以借用前面的计数
2、3214(连续低突然高):高了,那就要拿1和4重新比较了,看一下1变成4需要多少次(然后再和1之前已经的次数进行抵消)如果是负数,那就说明1不需要再进行变化即可超过4了。
3、连续高,那就连续像上面比较。
实现:分为两种情况:1、连续低的。2、忽然高的(要重新比较,但是还要抵消)
备注及收获:数学对数的应用:log2(3/4)=log2 3 - log2 4
2变成3所需要的次数,2变成4所需要的次数,相减就是4变成3的所需要的2的次数(高中数学的应用) 关于次幂类的数学问题都该想到log
该题还使用了强制转换和上下取整
思维题:
1、挑出几种情况,来猜测通项公式
2、缩小规模
3、看“至少”“边界”
4、假设

#include<bits/stdc++.h>
using namespace std;
#define ll long long
using i64 = long long;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
ll a[N],t[N];
 
int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int q; cin>>q;
    while(q--)
    {
        int n; cin>>n;//一共n个数字 
        for(int i = 1;i <= n; i++)
            cin>>a[i];//输入这个数组
			 
        for(int i = 2;i <= n; i++)
        {
            if(a[i] >= a[i-1])//如果忽高   这个减号就是抵消的意思 
                t[i] = max(0ll,(ll)(t[i-1]-floor(log2((double)a[i]/a[i-1]))));
            else//连续低                   数学的log(学了几年数学愣是没学懂) 
                t[i] = max(0ll,(ll)(t[i-1]+ceil(log2((double)a[i-1]/a[i]))));
        }
        ll ans = 0;
        for(int i = 1;i <= n; i++)
            ans += t[i];
        cout<<ans<<"\n";
    }
 
 
    return 0;
}

4、F题(这题我思路根本没想懂,下面是看着答案硬凑的,还是不太会)
题意:一个数组,找到其中的连续子集(这个子集是唯一的),这样的连续子集有多少个
思路:
唯一:什么是唯一?:一个子串和另一个子串,长度,和元素至少一个不同(第二个子串是可以不连续的,但是必须有序,这是题目意思吧)
1、如果元素相同,你必须控制长度不同:现在给你一个,左边的不能跟他替换(甩掉中间不连续,就是一个全等字符串),右边的本身就不能替换(中间不能甩掉,因为必须有序)
2、如果长度相同,你必须控制元素不同:现在给你一个固定长度的,左平移,如果左边就没有元素跟最左边的这个一样,那再怎么往左边平移都不会有重复(右边也一样)
答案思路:左边第一个出现的,和右边最后一个出现的,可以组成一个唯一性的数组
思维题:
序列这种东西,只能抓住左右点,或者长度
二维矩阵,迷宫啥的,只能抓住坐标

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
	int n; std::cin >> n;
	std::vector<int> a(n+1), f(n+2), g(n+2);
	for (int i = 1; i <= n; i++) {//现在写代码都开始从1输入数据了么?很能避免下标和常用习惯混淆! 
		std::cin >> a[i];//输入数组 
	}
	std::map<int, int> v;
	for (int i = 1; i <= n; i++) {
		f[i] = v[a[i]] == 0;//若该数字出现次数为0,即为正着来的首次出现 
		v[a[i]] += 1;//计数数组 
	}
	v.clear();//把v清空 
	for (int i = n; i; --i) {//逆向计数 
		g[i] = v[a[i]] == 0;//即为反着来的首次出现 
		v[a[i]] += 1;
	}
	v.clear();
	
	for (int i = n; i; --i) {
		g[i] += g[i+1];//后缀和  记录后面一串数字中 最后一次出现的数字有多少个 
	}					//为了下面正向走的时候和后面的进行组合 

	ll ans = 0;
	for (int i = 1; i <= n; i++) ans += 1LL * f[i] * g[i];
	std::cout << ans << '\n';
}
		                
signed main() {
	int _ = 1; std::cin >> _;
	while (_--) solve();
	return 0;
}

posted @ 2024-04-05 18:01  涤生yang  阅读(29)  评论(0)    收藏  举报