A - Halloumi Boxes
写这道题之前需要了解一下什么是 冒泡排序
然后我们把题读明白后就会发现,只要当k > 2的时候就必定可以模仿冒泡排序的过程,把原数组排列成有序的形式。
否则的话就要判断原序列是否已经有序
#include <bits/stdc++.h>
#define Start cin.tie(0), cout.tie(0), ios::sync_with_stdio(false)
using namespace std;
const int N = 2e5 + 10;
int a[N];
int main()
{
cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);//这里是优化cin和cout
int t = 1;
cin >> t;
while (t--)
{
int n,k;cin >> n >> k;
bool judge = true;
for(int i = 1 ; i <= n; i ++)
{
cin >> a[i];
if(i != 1)
{
if( a[i] < a[i - 1])judge = false;
}
}
if( !judge && k < 2 )
cout << "NO" <<'\n';
else
cout << "YES" <<'\n';
}
return 0;
}
B - StORage room
这道题是贪心,位运算
因为任何一个 M(i,j)都是由ai | aj得到的,或运算是取并集。
所以我们不妨贪心思考一下,我认为 ai 都是他对应一列的与起来的,因为与运算是取交集,当最坏情况都没有满足的时候本题必定无解。
#include <bits/stdc++.h>
#define Start cin.tie(0), cout.tie(0), ios::sync_with_stdio(false)
using namespace std;
const int N = 1e3 + 10;
int g[N][N];
int ans[N];
int main()
{
Start;int t = 1;
cin >> t;
while (t--)
{
int n; cin >> n;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
cin >> g[i][j] ;
//ans[1] = 0;
int idx = quickpow(2,30) - 1;
for(int i = 1; i <= n; i ++)
ans[i] = idx;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
if(i == j)continue;
ans[j] &= g[i][j];
}
}
bool judge = false;
for(int i =1 ; i <= n; i ++)
{
for(int j = i + 1; j <= n; j ++)
{
if( (ans[i] | ans[j]) != g[i][j])
{
judge = true;
break;
}
}
}
if( judge )
{
cout << "NO\n" ;
}
else
{
cout << "YES\n";
for(int i = 1; i<= n; i ++) cout << ans[i] << " ";
cout <<'\n';
}
}
return 0;
}
C - Theofanis' Nightmare
本题考查前缀和,贪心
通读题目,我们可知一个数前面的集合数量越多这个数的权重越大,那么我们可以反过来思考后缀和,当在一个数前面加一个集合的的时候使得后面增加的权重不为负,则应该加上一个集合。
例如
0 3 7 -9 2 4
我们可以发现,如果-9前面多一个集合那么后三个数字权重都+1,总增加就是(-9)+2+4 = -3,我们发现使得总权重小了,那么-9前面就不应该有一个集合,
对应的如果7前面多一个集合那么就是,7+(-9)+2+4答案就是4,使得最后答案增加了,所以应当加一个集合
还需要特判一下无论如何这些数都应处于一个集合内
#include <bits/stdc++.h>
#define Start cin.tie(0), cout.tie(0), ios::sync_with_stdio(false)
using namespace std;
const int N = 1e5 + 10;
int a[N];
signed main()
{
Start;int t = 1;
cin >> t;
while (t--)
{
int n; cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
int ans = 0;
int lmr = 0;
for(int j = n ; j >= 1; j --)
{
lmr += a[j];
if( lmr > 0 || j == 1) ans += lmr;
else continue;
}
cout << ans << '\n';
}
return 0;
}
D - Maximum And Queries (easy version)
位运算,贪心
题目数据规定了 n * q <= 1e5,那么可以认为最大的数据就是对长度为1e5的数组进行一次询问。
又因为 k <= 1e18,所以当数组中仅有一个数时,最坏情况就是1e18+1e6 < 2^60,所以开一个60位的数组记录二进制结果就行。
然后计算答案应该从二进制的高位开始计算,因为如果从低位开始计算,下一次计算的时候我们这一位填1会影响计算k的大小,但是如果从高位开始计算,如果这一位需要通过操作让其变为1,那么他的低位就会变为0,并且这样的变化并不会影响低位操作次数的统计。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,q;
cin>>n>>q;
vector<long long> a(n);
for(int i=0;i<n;i++)
cin>>a[i];
while(q--)
{
long long k;
cin>>k;
long long ans=0;
vector<long long> _a=a;
for(int i=60;i>=0;i--)
{
long long cost=0;
vector<long long> __a=_a;
for(int j=0;cost<=k&&j<n;j++)
{
long long t=(_a[j]>>i&1?0:(1ll<<i)-_a[j]%(1ll<<i));
cost+=t;
__a[j]+=t;
}
if(cost<=k)
{
ans+=1ll<<i;
k-=cost;
_a=__a;
}
}
cout<<ans<<endl;
}
return 0;
}