1900思维题

  • https://codeforces.com/problemset/problem/1718/A2

 

题意:

  • 给一个长度为n的数组a
  • 每次操作选择一个区间[L,R](1L leqRn),和一个整数x
  • 把区间所有的数异或上x,每次操作的的代价是RL+1/2
  • 问把数组a全部变为0的最小代价是多少

 

思路:

  • 观察发现[L,R]长度为1或2、3或4……的代价是相等的,而且还可以进一步推断,每一次的操作都可以分割成由1或2组成的小区间(在代价上数值相等)
  • 于是可见我们要分割的小区间长度为2的要尽可能的多(或者说每次操作都贪心少掉一次),才能达到代价最小
  • pre[i]=a[1]^a[2]^......^a[i],对于一个区间[L,R],当pre[L] == pre[R]时,容易发现这个区间操作的最小代价是R-L-1,否则是R-L
  • 于是题目就转化成了一个简单的线性dp,时间复杂度O(n^2)的做法呼之欲出
  • 但是显然O(n^2)是不太够用的,我们需要优化,用一个map来记录上一个前缀异或和最近的位置,从这个位置进行状态转移即可
  • 时间复杂度是O(n)

复制代码
#include<bits/stdc++.h>
#define debug1(a) cout<<#a<<'='<< a << endl;
#define debug2(a,b) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<endl;
#define debug3(a,b,c) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<"  "<<#c<<" = "<<c<<endl;
#define debug4(a,b,c,d) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<"  "<<#c<<" = "<<c<<"  "<<#d<<" = "<<d<<endl;
#define debug5(a,b,c,d,e) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<"  "<<#c<<" = "<<c<<"  "<<#d<<" = "<<d<<"  "<<#e<<" = "<<e<<endl;
#define fr(t, i, n)for (long long i = t; i < n; i++)
#define endl "\n"
#define fi first
#define se second
//#define int long long
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;

//#pragma GCC optimize(3,"Ofast","inline")
//#pragma GCC optimize(2)
const int N = 1e5+10;
int a[N],pre[N],f[N];

void solve() 
{
    int n;cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        pre[i] = pre[i-1] ^ a[i];
    }

    map<int,int> idx;
    idx[0] = 0;          //需要注意,刚开始的位置记录为0,0
    for(int i = 1;i <= n;i ++)
    {
        f[i] = f[i-1] + 1;
        if(idx.count(pre[i]))
        {
            f[i] = min(f[i],f[idx[pre[i]]] + i - idx[pre[i]] - 1);
        }
        idx[pre[i]] = i;
    }
    cout << f[n] << endl;
}

signed main()
{
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int T = 1;cin >> T;
    
    while(T--){
        //puts(solve()?"YES":"NO");
        solve();
    }
    return 0;

}
/*

*/
复制代码

  • 从这个O(n)的线性dp,能发现每个位置都是找上一个最近的相同异或前缀和的位置,那么我们可以直接省略掉这个dp的过程,直接用map记录
  • 答案就是  n - x([L,R],pre[L] == pre[R]最多的数量)
复制代码
#include<bits/stdc++.h>
#define debug1(a) cout<<#a<<'='<< a << endl;
#define debug2(a,b) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<endl;
#define debug3(a,b,c) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<"  "<<#c<<" = "<<c<<endl;
#define debug4(a,b,c,d) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<"  "<<#c<<" = "<<c<<"  "<<#d<<" = "<<d<<endl;
#define debug5(a,b,c,d,e) cout<<#a<<" = "<<a<<"  "<<#b<<" = "<<b<<"  "<<#c<<" = "<<c<<"  "<<#d<<" = "<<d<<"  "<<#e<<" = "<<e<<endl;
#define fr(t, i, n)for (long long i = t; i < n; i++)
#define endl "\n"
#define fi first
#define se second
//#define int long long
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;

//#pragma GCC optimize(3,"Ofast","inline")
//#pragma GCC optimize(2)
const int N = 1e5+10;
int a[N],pre[N];

void solve() 
{
    int n;cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        pre[i] = pre[i-1] ^ a[i];
    }

    set<int> st;st.insert(0);
    int ans = n;
    for(int i = 1;i <= n;i ++)
    {
        if(st.count(pre[i]))
        {
            ans --;
            st.clear();
        }
        st.insert(pre[i]);
    }
    cout << ans << endl;
}

signed main()
{
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int T = 1;cin >> T;
    
    while(T--){
        //puts(solve()?"YES":"NO");
        solve();
    }
    return 0;

}
/*

*/
复制代码

 

posted @   俄罗斯刺沙蓬  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
返回顶端
点击右上角即可分享
微信分享提示