目录
Problem - A - Codeforces
题意:给定长度为n的数组,要求把数组分成k个子段,并且让每个子段的绝对值之差的和最小。
思路:一开始拿到题目没有任何思路,只能先求出两两之间的绝对值之差,然后手玩了一下发现,要求分成k个子段正好是所有的绝对值之差减去(k-1)个最大的绝对值之差。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=1e9+7;
const int maxv=4e6+5;
int d[1005][1005];
void solve()
{
int n,k;
cin>>n>>k;
vector<int> a(n);
for(auto &x: a) cin>>x;
vector<int> b;
ll sum=0;
for(int i=1;i<n;i++){
int x=abs(a[i]-a[i-1]);
b.push_back(x);
sum+=x;
}
sort(b.begin(),b.end(),greater<int>());
for(int i=0;i<k-1;i++){
sum-=b[i];
}
cout<<sum<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
Problem - B - Codeforces
思路:贪心,我们可以知道,与运算,只有可能越运算越小,我们尽量让每次运算后的值为0即可,若出现了一次0,就重置一次,然后段数++,这样下来一定是最优的,最后注意特判一下段数为0的情况。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=1e9+7;
const int maxv=4e6+5;
int d[1005][1005];
void solve()
{
int n;
cin>>n;
vector<ll> a(n);
int cnt=0;
for(auto &x: a) cin>>x;
ll sum=a[0];
int f=0;
for(int i=0;i<n;i++){
ll val=sum&a[i];
if(val==0){
cnt++;
sum=a[i+1];
}
else{
sum=val;
}
}
if(!cnt) cnt++;
cout<<cnt<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
Problem - C - Codeforces
题意:对于某段从i-n的区间,生成一个新的数,为区间(i-n)的异或和,题目要求求出按照上述要求最大的异或和。
思路:对于某段从i-n的区间,异或和为^
^........^
,对于j<i时,异或和为aj^……an,又因为j<i,所以对于区间i-n的异或和被抵消了,则最后添加的是区间(j-i-1),那么此题就转化为了求子段的异或和最大值。
对于子区间异或和,有一个前置知识,即任意两数异或和最大值。
因为
数组的前缀异或和有这样一个规律:
设 x o r [ i ] = a [ 1 ] ⊕ a [ 2 ] ⊕ . . . ⊕ a [ n] ,则子数组 a r r [ i . . j ] 的异或和为:x o r [ j ] ⊕ x o r [ i − 1 ]
于是我们可以这样做:求以每个元素作为子数组结尾的子数组的最大异或和,通过将 x o r [ 1 ] , x o r [ 2 ] , . . . , x o r [ i − 1 ] 分别插到 Trie 树中,然后查询 x o r [ i ] 与 Trie 树中的元素的最大异或值,此时就变成了上述 最大异或对 模型。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=1e9+7;
const int maxv=4e6+5;
vector<int> a(N);
int son[N*31][2],idx;
int n;
int s[N];
void add(int x)
{
int p=0;
for(int i=31;~i;i--){
int &s=son[p][x>>i&1];
if(!s) s=++idx;
p=s;
}
}
int query(int x)
{
int p=0,res=0;
for(int i=31;~i;i--){
int c=x>>i&1;
if(son[p][!c]){
res+=1<<i;
p=son[p][!c];
}
else p=son[p][c];
}
return res;
}
void solve()
{
cin>>n;
idx=0;
for(int i = 0; i < n*31; i ++){
//cnt[i] = 0;
son[i][1] = 0;
son[i][0] = 0;
}
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]^a[i];
}
int res=0;
add(s[0]);
for(int i=1;i<=n;i++){
res=max(res,query(s[i]));
add(s[i]);
}
cout<<res<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}
解法二:因为此题值域比较小,所以可以直接暴力求出答案。
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
using LL = long long;
int main(){
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int T;
cin >> T;
while(T--){
int n;
cin >> n;
vector<int> a(n);
for(int i = 0; i < n; i++) cin >> a[i];
vector<bool> v(256);
v[0] = true;
int s = 0, ans = 0;
for(int i = 0; i < n; i++){
s ^= a[i];
v[s] = true;
for(int j = 0; j < 256; j++){
if (v[j]){
ans = max(ans, s ^ j);
}
}
}
cout << ans << '\n';
}
}
再给出一道和异或有关的题目:
题意:有两个操作:
操作1:对从x-y上的简单路径上的权值w进行异或。
操作2:给定点x,求出点周围权值的异或和。
思路:我们可以发现,对一条路径上的权值进行异或,边只异或了一次,但是对于除端点的点而言,每个点都是异或了两次,相当于没变,所以我们只需要对给定的端点进行修改即可。
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=3e6+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=1e9+7;
const int maxv=4e6+5;
int c[N];
void solve()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n-1;i++){
int a,b,w;
cin>>a>>b>>w;
c[a]^=w,c[b]^=w;
}
while(q--){
int op;
cin>>op;
if(op==1){
int x,y,z;
cin>>x>>y>>z;
c[x]=c[x]^z,c[y]=c[y]^z;
}
else {
int x;
cin>>x;
cout<<c[x]<<endl;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}