日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

牛客练习赛29 题解

發布時間:2023/12/3 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 牛客练习赛29 题解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

牛客練習賽29

A. 可持久化動態圖上樹狀數組維護01背包

題解

這題跟標題沒有任何關系…

貪心的使得負數刪除的時候下標盡可能大,然后正數的時候下標盡可能小.

觀察到每個數下標最大的時候就是它的初始下標,下標的最小值是1.

然后貪心一下就好了.

代碼

#include <iostream> using namespace std; long long a[1000007]; int main(){ios::sync_with_stdio(false);int n;cin >> n;long long sum = 0;for(int i = 1;i <= n;++i){cin >> a[i];if(a[i] >= 0) sum += a[i];else sum += a[i] * i;}cout << sum << endl; }

B. 列隊

題解

有點迷,WA了還沒改出來…QAQ

C.枇杷

題解

還沒看題

D.禁止動規

題解

根據裴蜀定理,當存在一些x的組合,使得它們的gcd為1的時候,p1x1+p2x2+...+pnxn=1p_1x_1 + p_2x_2 + ... + p_nx_n = 1p1?x1?+p2?x2?+...+pn?xn?=1,方程一定有解.

因此我們就求gcd(x1,x2,...,xn)=1gcd(x_1,x_2,...,x_n) = 1gcd(x1?,x2?,...,xn?)=1的方案數即可

f(x)f(x)f(x)表示gcd(x1,x2,...,xn)=xgcd(x_1,x_2,...,x_n) = xgcd(x1?,x2?,...,xn?)=x的方案數

然后莫比烏斯反演

(?mx?)n=∑x∣df(d)(\lfloor \frac{m}{x} \rfloor )^n = \sum_{x|d}{f(d)}(?xm??)n=xd?f(d)

得到

f(x)=∑x∣dμ(dx)?(?md?)nf(x) = \sum_{x|d}{\mu(\fracozvdkddzhkzd{x})*(\lfloor \frac{m}ozvdkddzhkzd \rfloor )^n}f(x)=xd?μ(xd?)?(?dm??)n

答案就是
f(1)=∑d=1mμ(d)?(?md?)nf(1) = \sum_{d=1}^{m}\mu(d)*(\lfloor \frac{m}ozvdkddzhkzd \rfloor )^nf(1)=d=1m?μ(d)?(?dm??)n

(?md?)(\lfloor \frac{m}ozvdkddzhkzd \rfloor )(?dm??)分塊,然后杜教篩求莫比烏斯函數前綴和就可以了.

代碼

#include <iostream> #include <algorithm> #include <cstring> #include <unordered_map> #include <map> #define pr(x) std::cout << #x << ':' << x << std::endl #define rep(i,a,b) for(int i = a;i <= b;++i) #define clr(x) memset(x,0,sizeof(x)) #define setinf(x) memset(x,0x3f,sizeof(x))typedef long long ll; typedef unsigned long long ull; const int N = 10000000; ull mu[N+10];int prime[N+10],pcnt,zhi[N+10]; void sieve(){pcnt = 0;mu[1] = zhi[1] = 1;for(int i = 2;i <= N;++i){if(!zhi[i]) mu[i] = -1,prime[pcnt++] = i;for(int j = 0;j < pcnt && prime[j]*i <= N;++j){zhi[i*prime[j]] = 1;if(i % prime[j] == 0){mu[i*prime[j]] = 0;break;}else{mu[i*prime[j]] = -mu[i];}}}for(int i = 1;i <= N;++i) mu[i] += mu[i-1]; } std::unordered_map<ll,ull> rec,vis; ull Mu(ll x){if(x <= N) return mu[x];if(vis[x]) return rec[x];ull res = 1,now = x,nxt;while(now >= 2){nxt = x/(x/now+1);res -= (now - nxt) * Mu(x/now);now = nxt;}vis[x] = 1;return rec[x] = res; } ll n,m; ull mod_pow(ull x,ll n) {ull res = 1;while(n) {if(n & 1)res *= x;x *= x;n >>= 1;}return res; } int main() {sieve();unsigned long long ans = 0;std::cin >> n >> m;ll nxt;for(ll x = m;x >= 1;x = nxt) {nxt = m/(m/x+1);ans += mod_pow(m/x,n) * (ull)(Mu(x) - Mu(nxt));}std::cout << ans << std::endl;return 0; }

E. 位運算?位運算!

題解

補過去了,用這種做法發現被卡常,然后優化了一下常數+快速讀入,用了2400ms的樣子過掉了.

又是一個新套路,之前不會的新套路.

考慮到要進行區間與區間或還要進行區間求和,那么我們就必須將位拆開來看.

nnn個數拆成202020顆線段樹,第iii顆線段樹維護這nnn個數上第iii位的信息,線段樹區間[l,r][l,r][l,r]表示[l,r][l,r][l,r]區間內的數第iii位上有多少個111.
這樣求和就很方便了,按位算貢獻即可.

或操作,就相當于把指定線段樹的指定區間覆蓋成111.與操作就相當于把指定線段樹的指定區間覆蓋成000.這里在線段樹上打lazylazylazy標記即可.

現在問題在于,如何進行循環移位.

我們發現循環移位就相當于線段樹之間輪換兒子.

我們可以遞歸的找到這202020個線段樹在該區間下所包含的(完整線段最多lognlognlogn段),然后將它們進行輪換.

代碼

#include <iostream> #include <algorithm> #include <vector> #include <cstdio> #include <cstring> #include <assert.h> #define pr(x) std::cout << #x << ':' << x << std::endl #define rep(i,a,b) for(int i = a;i <= b;++i) const int N = 200007; int a[N],root[20],tot; struct node{int sum,lazy,lch,rch;node(int sum = 0,int lazy = 0,int lch = 0,int rch = 0):sum(sum),lazy(lazy),lch(lch),rch(rch){} }ns[N*80]; void tag(int o,int len,int lazy) {if(!o) return ;if(lazy == 1)ns[o].sum = len;elsens[o].sum = 0;ns[o].lazy = lazy; } void pushdown(int o,int l,int r) {if(ns[o].lazy) {int mid = (l + r) >> 1;tag(ns[o].lch,mid-l+1,ns[o].lazy);tag(ns[o].rch,r-mid,ns[o].lazy);ns[o].lazy = 0;} } void set(int o,int l,int r,int cl,int cr,int lazy){if(r < cl || cr < l)return ;if(cl <= l && r <= cr){tag(o,r-l+1,lazy);return ;}int mid = (l + r) >> 1;pushdown(o,l,r);set(ns[o].lch,l,mid,cl,cr,lazy);set(ns[o].rch,mid+1,r,cl,cr,lazy);ns[o].sum = ns[ns[o].lch].sum + ns[ns[o].rch].sum; }int query(int o,int l,int r,int ql,int qr) {if(r < ql || qr < l)return 0;if(ql <= l && r <= qr)return ns[o].sum;int mid = (l + r) >> 1;pushdown(o,l,r);int lch = query(ns[o].lch,l,mid,ql,qr);int rch = query(ns[o].rch,mid+1,r,ql,qr);return lch + rch; }void build(int &o,int l,int r,int p) {if(!o)o = ++ tot;if(l == r) {ns[o] = node((a[l]>>p)&1,0,0,0);return ;}int mid = (l + r) >> 1;build(ns[o].lch,l,mid,p);build(ns[o].rch,mid+1,r,p);ns[o].sum = ns[ns[o].lch].sum + ns[ns[o].rch].sum; }void shift(int *os[],int l,int r,int cl,int cr,int x) {if(r < cl || cr < l)return ;if(cl <= l && r <= cr) {int tmp[20];rep(i,0,19) tmp[i] = *os[i];rep(i,0,19)*os[i] = tmp[(i+x+20)%20];return ; }int mid = (l + r) >> 1;rep(i,0,19)pushdown(*os[i],l,r);int *lft[20],*rgt[20];rep(i,0,19)lft[i] = (&(ns[*os[i]].lch)),rgt[i] = (&(ns[*os[i]].rch));shift(lft,l,mid,cl,cr,x);shift(rgt,mid+1,r,cl,cr,x);rep(i,0,19)ns[*os[i]].sum = ns[ns[*os[i]].lch].sum + ns[ns[*os[i]].rch].sum; } int n,q; inline int read() {char ch = getchar(); int x = 0, f = 1;while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();} while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();} return x * f; } int main () {n = read();q = read();rep(i,1,n)a[i] = read();rep(i,0,19)build(root[i],1,n,i);rep(i,1,q) {int op,l,r,v;op = read(),l = read(),r = read(),v = read();if(op == 1 || op == 2) {int *os[20];rep(i,0,19)os[i] = &root[i]; shift(os,1,n,l,r,v*(op == 1?1:-1));} else if(op == 3) {rep(i,0,19)if((v >> i) & 1)set(root[i],1,n,l,r,1);}else if(op == 4) {rep(i,0,19)if(((v >> i) & 1) == 0)set(root[i],1,n,l,r,-1);}else {long long ans = 0;rep(i,0,19)ans += query(root[i],1,n,l,r) * (1LL<<i);printf("%lld\n",ans);}} }

F. 算式子

題解

對于每一個x∈[1,m]x \in [1,m]x[1,m],都要計算下面的式子.
∑i=1n(?aix?+?xai?)\sum_{i=1}^{n} (\lfloor \frac{a_i}{x} \rfloor + \lfloor \frac{x}{a_i} \rfloor)i=1n?(?xai???+?ai?x??)

這個式子可以拆成兩部分來求,即
?aix?\lfloor \frac{a_i}{x} \rfloor?xai????xai?\lfloor \frac{x}{a_i} \rfloor?ai?x??.

第一部分容易求

xxx固定時計算介于[x,2x),[2x,3x)....[x,2x),[2x,3x)....[x,2x),[2x,3x)....aia_iai?有多少個,講貢獻計入ans1[x]ans_1[x]ans1?[x].

第二部分就不那么直觀了

我們設ans2[x]=∑i=1n?xai?ans_2[x] = \sum_{i=1}^n \lfloor \frac{x}{a_i} \rfloorans2?[x]=i=1n??ai?x??

對于每一個aia_iai?其中x∈[ai,2ai)x \in [a_i,2a_i)x[ai?,2ai?)ans2ans_2ans2?應該得到影響111,所以我應該給ans2[ai,2ai)ans_2[a_i,2a_i)ans2?[ai?,2ai?)數組進行區間+1+1+1.而x∈[ai,2ai)x \in [a_i,2a_i)x[ai?,2ai?)的應該得到影響222,所以我應該給ans2[2ai,3ai)ans_2[2a_i,3a_i)ans2?[2ai?,3ai?)數組進行區間+2+2+2…依次類推…
具體實現的話,我們線段ans2ans_2ans2?求一個差分數組deltadeltadelta,那么我對于ans2ans_2ans2?[l,r][l,r][l,r]區間+d+d+d,就相當于對差分數組delta[l]+=d,delta[r+1]?=ddelta[l]+=d,delta[r+1]-=ddelta[l]+=d,delta[r+1]?=d,最后,對差分數組求前綴和就可以恢復成ans2ans_2ans2?數組.

總的時間復雜度O(nlogn)O(nlogn)O(nlogn)

代碼

#include <iostream>const int N = 2000007; int n,m; long long a[N]; long long num[N],cnt[N],cnt2[N]; long long ans[N]; int main() {std::ios::sync_with_stdio(false);std::cin >> n >> m;for(int i = 0;i < n;++i) {std::cin >> a[i];cnt[a[i]] ++ ;num[a[i]] ++ ;}for(int i = 0;i <= m;++i) cnt[i] += cnt[i-1];long long res = 0;for(int x = 1;x <= m;++x) {int y = 2*x;for(;y <= m;y += x) {ans[x] += (cnt[y-1] - cnt[y-x-1])*((y-1)/x);}ans[x] += (cnt[m] - cnt[y-x-1])*(cnt[m]/x);}for(int i = 1;i <= m;++i) {cnt2[i] += num[i];for(int j = 2*i;j <= m;j += i) {cnt2[j] += num[i];}}for(int x = 1;x <= m;++x) {cnt2[x] += cnt2[x-1];ans[x] += cnt2[x];res ^= ans[x];}std::cout << res << std::endl;return 0; }

總結

以上是生活随笔為你收集整理的牛客练习赛29 题解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。