A. The Man who became a God
题意:输入给出包含n个数的数组,和要分成的区间个数k,定义区间权值为所有相邻两个数的差值的和,要求将数组分为k个子区间使得所有子区间的权值和最小
思路:将一个权值为x的区间分为两个子区间,假设两个子区间相邻处的两个元素的差值为y,两个子区间的权值分别为a和b,则有x=a+b+y,则若想要使a+b最小,就要使y最大,所以本题只需要将所有的相邻元素差求出来,然后按照从大到小的顺序找到前n-k个差,这n-k个差所在的位置就是要将区间分开的边界
ac代码:
#include
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair pii;
const int N=1100;
int n,k;
int b[N];//标记断点
int a[N];
void solve()
{
vector g;
cin>>n>>k;
memset(b,0,sizeof b);
for(int i=0;i>a[i];
if(i)g.pb({abs(a[i]-a[i-1]),i});
}
if(n==k)
{
cout<<0<=g.size()-k+1;i--)
{
b[g[i].second]=1;//标记断点
}
// for(int i=0;i>T;
while(T--)
{
solve();
}
}
B. Hamon Odyssey
题意:给出包含n个数的数组,要求将该数组划分为数个子区间,要求将所有区间进行与运算之后求和,使得和最小并且区间数最多
trick:与运算:0&0=0,1&0=0,0&1=0,1&1=1,由此可以发现,与运算存在性质:0在与运算之后不可能变为1,而1与运算之后可能变为0,所以对一个数进行与运算,只会越运算越小或不变,而不会变大。
思路:本题是在要求与和最小的前提下使区间数最多,所以首先要考虑这个前提,既然与运算只会越运算越小,所以对于本题,考虑两种情况:1、f(1,n)!=0,既然所有数全部进行与运算之后结果仍然不为零,则将整段区间分为任意情况的子区间,可知每个子区间的与都会大于f(1,n),自然与的和也会大于f(1,n),所以对于f(1,n)!=0的情况,结果为1。2、f(1,n)=0,既然整段区间的与已经达到了最小,则此时再考虑怎么分才能使得区间数最多,既然题目要求的是子区间而不是子序列,所以只需要从前往后遍历一遍,找出所有与为1的子区间即可,特殊处理如果最后剩下的区间与不为0,则将该区间与前面的一个与为0的区间合并为一个区间即可,因为0与任何数都为0
ac代码:
#include
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair pii;
const int N=2e5+10;
int n;
int a[N];
void solve()
{
cin>>n;
int now;
for(int i=0;i>a[i];
if(!i)now=a[i];
else now&=a[i];
}
if(now)cout<<1<>T;
while(T--)
{
solve();
}
}
C. Vampiric Powers, anyone?
题意:输入给出n个非负整数,下标1~n,可以进行任意次以下操作:在数组末尾添加一个原数组的任意后缀异或和,要求输出可以得到的最大后缀异或和
trick:异或是一个可逆运算,如果a^b=c,则c^b=a,也就是一个数两次异或同一个数,就相当于没有异或这个数,所以可以通过前缀异或和来求区间异或和
思路:本题可以通过两次后缀异或和操作来实现区间异或和操作,若要得到xor[l-r],可以通过a[n+1]=xor[r+1,n],则a[n+2]=xor[l,n+1]=xor[l-r],则该题就转化成了求原数组的最大子区间异或和,而本题数组中的数最大不超过256,也就是说任意子区间的异或和都不会超过256,我们可以用一个set来存已经出现过的前缀异或和,不同前缀区间有相同的异或和可以只做一次运算,因为本题不需要找出具有最大异或和的区间是哪一段
ac代码:
#include
#define endl '\n'
#define ll long long
#define INF 0x3f3f3f3f
#define pb push_back
#define int long long
#define Mirai ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef pair pii;
const int N=1e5+10;
int a[N];
int n;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
set s;
int ans=0;
s.insert(0);
for(int i=1;i<=n;i++)
{
a[i]^=a[i-1];
for(auto j:s)
{
ans=max(ans,a[i]^j);
//将此前缀异或和与所有之前出现过的前缀异或和进行异或
//可以得到以当前点之前的所有点为起点,以当前点为终点的区间的异或和
//而我们只需要得到最大异或和,而这些前缀异或和中又有很多重复数字
//所以用set来保存出现过的前缀异或和进行异或即可
}
s.insert(a[i]);
}
cout<>T;
while(T--)
{
solve();
}
}