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;
}