中石油训练赛 - 小A盗墓(线段树+异或结论)
題目鏈接:點擊查看
題目大意:給出n個數,以及m個操作,每個操作分為兩種:
題目分析:因為涉及到了區間修改和區間查詢的問題,所以我們首先想到利用線段樹來輔助解決,第一個操作很簡單,單點修改,主要是我們該怎么樣在規定時間內完成第二個操作才是問題,這個題目的n和m給的都是5e5,那么我們每次查詢最多只能以logn的復雜度查詢,就已經達到2e7的時間復雜度了,線段樹已經占用掉了logn的復雜度,所以留給我們的只能用O(1)的復雜度來判斷這個條件了。我想過能不能用歸并樹來實現這個題,應該不大行,畢竟是已經被淘汰了的數據結構,之前興致勃勃的用歸并樹做主席樹的題,很失望的T掉了555。說回這個題,我們需要知道一個結論,就是從1~n異或出來的結果是有規律的,詳情請看我的上個博客,有了這個就好判斷了,我們可以用線段樹維護一個最小值,一個區間和,一個異或值,判斷的時候,我們可以通過最小值和區間長度判斷出最大值(因為數都是連續的,所以最小值加上區間長度就是最大值了。。),然后首先利用等差數列求和公式求一下這段的區間和應該是多少,再和線段樹中儲存的比較一下,如果相等再接著用異或的那個規律比較一下,這樣就能通過O(1)判斷是否滿足條件了。
對了有個細節需要注意一下,若求0~n區間異或的函數是xor_n(int n),那么我們想求x~y區間內的異或,應該是xor_n(y)^xor_n(x-1)
而不是xor_n(y)-xor_n(x-1),想一想,為什么?
好了直接上代碼了:
#include<iostream> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e5+100;struct Node {int l,r;int mmin,xor_;LL sum; }tree[N<<2];void pushup(int k) {tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);tree[k].xor_=tree[k<<1].xor_^tree[k<<1|1].xor_;tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;if(l==r){scanf("%d",&tree[k].mmin);tree[k].sum=tree[k].xor_=tree[k].mmin;return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k); }void update(int k,int pos,int val) {if(tree[k].l==tree[k].r){tree[k].sum=tree[k].mmin=tree[k].xor_=val;return;}int mid=tree[k].l+tree[k].r>>1;if(mid>=pos)update(k<<1,pos,val);elseupdate(k<<1|1,pos,val);pushup(k); }int querymin(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return inf;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].mmin;return min(querymin(k<<1,l,r),querymin(k<<1|1,l,r)); }int queryxor(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].xor_;return queryxor(k<<1,l,r)^queryxor(k<<1|1,l,r); }LL querysum(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].sum;return querysum(k<<1,l,r)+querysum(k<<1|1,l,r); }int xor_n(int n) {int temp=n&3;if(temp&1)return temp/2^1;return temp/2^n; }bool check(int len,int mmin,int xor_,LL sum) {int mmax=mmin+len-1;LL ssum=(1LL)*len*mmin+(1LL)*len*(len-1)/2;if(ssum!=sum)return false;int _xor_=xor_n(mmax)^xor_n(mmin-1);if(_xor_!=xor_)return false;return true; }int main() {int n,m;scanf("%d%d",&n,&m);build(1,1,n);while(m--){int op,x,y;scanf("%d%d%d",&op,&x,&y);if(op==1)update(1,x,y);else{int mmin=querymin(1,x,y);int xor_=queryxor(1,x,y);LL sum=querysum(1,x,y);if(check(y-x+1,mmin,xor_,sum))printf("Yes\n");elseprintf("No\n");}}return 0; }?
總結
以上是生活随笔為你收集整理的中石油训练赛 - 小A盗墓(线段树+异或结论)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 区间1~n的异或值
- 下一篇: qduoj - 今晚一起打CF吧——Co