A. Halloumi Boxes
题意
给定一个包含n个元素的数组,每次可以最多可以反转数组中的k个元素,操作次数无上限,询问是否可以将数组变成非递增排列
分析
对于操作次数无上限的问题,我们往往采用贪心的策略,可以发现只要每次反转两个元素,就一定可以将数组变为非递增的,因此只要操作次数大于等于2,就一定可以,对于操作次数<2的,除非原数组就是非递增数组,否则一定不可以
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>PII;
const int N=110;
int f[N];
int main()
{
int t;cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>f[i];
if(is_sorted(f+1,f+1+n) || k>=2) puts("YES");
else puts("NO");
}
return 0;
}
B. StORage room
题意
给定一个n行n列的矩阵m,除了主对角线全部为0之外,其余的m[i][j]都等于a[i] | a[j],请你判断是否有合法的a数组,如果有输出yes与其元素,如果没有则输出no
分析
第一次做到位运算的题真是无从下手,是一道不错的构造题
或运算的性质,只要两个数有一个是1结果就是1
首先根据第二组样例
0 | 3 | 3 | 5 |
3 | 0 | 3 | 7 |
3 | 3 | 0 | 7 |
5 | 7 | 7 | 0 |
第一行从第二列开始所代表的意义分别为
a[1] | a[2]=3 a[1] | a[3]=3 a[1] | a[4]=5
其中3=11,5=101
由于或运算的性质,只要两个数其中有一位该位是1,那么结果该位也一定是1,为了使答案最优(更容易满足其他元素),所以我们可以确定a[1]的第1位和第3位为1,那我们只需要找到a[1]上哪几位是0即可,对于该行所有元素如果相同位都为1,那么a[1]该位也为1,有一位为0,那么a[1]该位也为0。所以结论就是a[i]=矩阵m的第i行元素相与(除了对角线)
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>PII;
const int N=1e3+10;
int f[N][N];
int ans[N];
int main()
{
int t;cin>>t;
while(t--)
{
int n;cin>>n;
for(int i=1;i<=n;i++) ans[i]=-1;//初始化为-1
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>f[i][j];
if(j==i) continue;
if(ans[i]==-1) ans[i]=f[i][j];
else ans[i]=ans[i] & f[i][j];
}
}
int flag=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) continue;
if((ans[i] | ans[j])!=f[i][j])
{
flag=0;
break;
}
}
}
if(flag)
{
puts("YES");//答案最小为0
for(int i=1;i<=n;i++) cout<<max(ans[i],0)<<" ";
cout<<endl;
}
else puts("NO");
}
return 0;
}
C. Theofanis' Nightmare
题意
给定一个数组,请你将数组划分为若干个区间,定义每个区间的价值为每个区间内元素之和乘该区间的下标,求最大的总价值是多少
分析
假设将原数组划分为了4个区间,用mi表示第i个区间的元素之和,那么总价值为:m1*1+m2*2+m3*3+m4*4,
可等价于
m1+m2+m3+m4 +m2+m3+m4 +m3+m4 +m4
再分析一下划分区间的代价
对于a b,如果我们不划分区间,那么总价值为 a+b
如果我们将ab划分为两个区间,那么总价值为 a+b*2,即a+b+b
可以发现,如果我们选择在第i个元素处划分区间,那么产生的总价值会在原先的总价值上加一个i元素之和的所有元素之和,显然当第i个元素之后的元素大于0时,划分区间能得到更多的价值,所以我们用后缀和记录每个元素之后的元素之和,如果其后缀和大于0,那么我们在这里划分区间即可
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>PII;
int main()
{
int t;cin>>t;
while(t--)
{
int n;cin>>n;
vector<ll>a(n+1);
vector<ll>b(n+2);
b[n+1]=0;//防止越界
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=n;i>=1;i--) b[i]=b[i+1]+a[i];//后缀和
ll ans=0;
for(int i=1;i<=n;i++)
{
ans+=a[i];
if(b[i+1]>0) ans+=b[i+1];
}
cout<<ans<<endl;
}
return 0;
}
D1. Maximum And Queries (easy version)
题意
给定一个大小为n的数组,和m次询问,每次询问给定一个整数k,代表最多可操作k次,每次操作可以将数组中的一个数加1,求最多进行k次操作后,数组中所有元素相与的最大值
分析
对于相与得到的结果,只有每一位都是1时,才能产生贡献,为求相与的最大值,我们可以从高位枚举,求出将所有元素的该位变为1需要的操作次数之和是否小于k,想法不是很难,实现起来很麻烦,需要对位运算十分熟练
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
//ll a[N];
//ll b[N];
int main()
{
ll n,m;
cin>>n>>m;
vector<ll> a(n+1);
vector<ll>b(n+1);
for(int i=1;i<=n;i++) cin>>b[i];
while(m--)
{
for(int i=1;i<=n;i++) a[i]=b[i];
ll op;cin>>op;
ll res=0;
for(int k=60;k>=0;k--)//从高位开始判断
{
ll ans=0;//将a的所有元素的第k位都变成1的操作次数
for(int i=1;i<=n;i++)
{
if((a[i]>>k&1)==0) //a[i]的第k位为0
{
ans=ans+(1ll<<k)-a[i]%(1ll<<k);//将这一位变成1所需要的操作次数
if(ans>op) break;//操作次数大于op直接退出
}
}
if(ans<=op)//操作次数小于op
{
op-=ans;//剩下的操作次数
res+=1ll<<k;//将当前这一位都变成1之后产生的贡献
for(int i=1;i<=n;i++)
{
if((a[i]>>k&1) == 0) a[i]=0;//将这个元素第k位变为1之后,第k位之后都是0,所以直接变成0也不影响后续位的操作
}
}
}
cout<<res<<endl;
}
return 0;
}
寒假vp第一场