2016年ACM/ICPC大連賽區 E題(閱讀理解+樹狀陣列原理)
阿新 • • 發佈:2018-12-13
題意:
題意是個啥?我怎麼讀不懂?
這道題沒過的都是因為沒讀懂題吧!!!!!!簡直考閱讀理解啊!!!
賽後參考(茶飄香~)大佬的部落格終於讀懂了題意:
題意:每次查詢有兩種操作
op1:求加入L~R的數時所消耗的單元
op2:求將x加入集合或移動到其它集合所消耗的單元(即由x引起消耗的單元)
思路:op1:每次加入一個數i 那麼會移動[i-lowbit(i)+1 , i-1] ,總的消耗是i-(i-lowbit(i)+1) +1=lowbit(i) 所以每次加入一個數對應的消耗是2的冪次,那麼求L~R即可以列舉冪次,即: ans+=(n/(1<<i)-n/(1<<(i+1)))*(1<<i)
解釋一下,n/(1<<i)-n/(1<<(i+1))表示長為2^i的消耗的數的個數,例如:n=10 , 包含長為2的數是2,6,10 為什麼4,8不是,因為它們雖然是2的倍數,但更是4的倍數,包含更長的區間了,所以這部分要減去。
op2:由樹狀陣列可知 [i-lowbit(i)+1 , i-1] 是以i為根節點對應的區間,如果假如的數能夠移動i ,那麼這個數對應的孩子區間一定包含i ,所以從x向上一直找父節點即可。
程式碼:
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f3f3f3f3fLL using namespace std; const int maxn=100010; const ll mo=1e9+7; ll n,m,k,q; ll ans,tmp,cnt; ll lb(ll x){return x&(-x);} ll query(ll x) { ll ans=0; while(x<=n) { ans++; x+=lb(x); } return ans; } ll cal(ll x) { ll tmp=1,ans=0; while(tmp<=x) { ans+=((x/tmp)-(x/(tmp<<1)))*tmp; tmp<<=1; } return ans; } int main() { while(scanf("%lld%lld",&n,&q)!=EOF) { int op; ll x,y; while(q--) { scanf("%d",&op); if(op==1) { scanf("%lld%lld",&x,&y); printf("%lld\n",cal(y)-cal(x-1)); } else { scanf("%lld",&x); printf("%lld\n",query(x)); } } } return 0; }