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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HGOI20190707 题解

發(fā)布時間:2025/3/15 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HGOI20190707 题解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Problem A 鋼鐵俠的誕生

現(xiàn)在有$n$個數(shù)字$a_i \leq 10^9 $,然后取出$m$個數(shù)字,保證合法。

從小到大輸出剩余的$n-m$個數(shù)字。?

對于100%的數(shù)據(jù)$m\leq n \leq 3\times 10^5$

Sol : 直接map映射然后用iterator來遍歷整個map輸出答案即可。

復(fù)雜度大概是$O(n log_2 n)$

# pragma GCC optimize(2) # include<bits/stdc++.h> using namespace std; int n,m; map<int,int>mp; int main() {scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) {int t; scanf("%d",&t);mp[t]++;}for (int i=1;i<=m;i++) {int t; scanf("%d",&t);mp[t]--;}map<int,int>::iterator it;for (it=mp.begin();it!=mp.end();++it) {pair<int,int>p=*it;for (int j=1;j<=p.second;j++) printf("%d ",p.first);}return 0; } a.cpp

Problem B?鋼鐵俠的逃離

設(shè)函數(shù)$f(x) = $x的二進(jìn)制中"1"的個數(shù)。 求 $\sum\limits_{i=1}^n f(B+i\times A)$ 的值。

對于100%的數(shù)據(jù)$A\leq 10^4 ,B \leq 10^{16} n\leq 10^{12} $?

Sol : 對于每一個二進(jìn)制位分別考慮,我們想法是對于二進(jìn)制數(shù)第$k$位 $ B+A $和 $ B+(2^k+1)A $是相同的。

證明的話直接可以把兩個數(shù)同時減去一個A,然后就變成了$B$和$B+(2^k)A$第$k$位的關(guān)系。

左式的$2^k A$相當(dāng)于把A向左移k位,這個時候第k位一定是B的k位的數(shù)+0 = B的k位的數(shù)。 那么就不會發(fā)生變化。

所以我們只需要考慮一個循環(huán)節(jié)$B+A ... B+2^k A$中1的個數(shù)即可。

對于每個循環(huán)節(jié),顯然(也可以通過打表發(fā)現(xiàn))每一位連續(xù)是1連續(xù)是0的可能比較大。所以我們考慮對于連續(xù)的1和連續(xù)的0一起處理。

設(shè)$f(a,b,k,r) = \sum\limits_{i=0} ^ {r} b+a\times i 中第k位1的個數(shù)?$?

考慮如何算$f(a,b,k,r)$? . 首先我們需要在每一個01塊內(nèi)用O(1)的時間完成跳躍。

于是考慮什么時候是01的分界點。由于進(jìn)位的問題所以每一個01塊分界點一定是以$2^k$一個循環(huán)的,并且是$2^k$的倍數(shù)。

考慮一個位置 b?我們什么時候才能跳出這個塊呢? 比該點更加靠右的$2^k$倍數(shù)的點可以輕易算出 $2^k \times (\left \lfloor \frac{b}{2^k}? ?\right \rfloor\times +1 )?$ 就是下一個01分界點的坐標(biāo)。

注意到01分界點事實上已經(jīng)完成了進(jìn)位即完成了01轉(zhuǎn)換。

所以如果要跳到嚴(yán)格的下一個01分界點的右側(cè),我們需要跳躍$c= \frac{2^k \times (\left \lfloor \frac{b}{2^k}? ?\right \rfloor +1 ) -1 }{a}+1$步

所以,對于$f(a,b,k,r)$的求法已經(jīng)非常明確了:

$f(a,b,k,r) = \left\{\begin{matrix} b\ and \ 2^k = 0 \left\{\begin{matrix} 0 & r-c<0\\? \ \ \ f(a,b+c\times a,k,r-c)\? \? & r \geq c \end{matrix}\right.\\? b\ and \ 2^k = 1 \left\{\begin{matrix} r+1 & r-c<0\\ c+f(a,b+c\times a,k,r-c)& r \geq c \end{matrix}\right.\\ \end{matrix}\right.$

由于是從0開始的,所以n必須等于n-1 ,當(dāng)前是從b+a開始的,所以是b=b+a

答案就是$ans = \sum\limits_{i=0}^{bit\_num(a+nb)} f(a,b,i,2^{i+1}-1)\times \left \lfloor \frac{n}{2^{i+1}} \right \rfloor+ f(a,b+\left \lfloor \frac{n}{2^{i+1}} \right \rfloor \times 2^{i+1} \times a,i,n \mod 2^{i+1})$

# include <bits/stdc++.h> # define int long long using namespace std; int fun(int a,int b,int k,int r){int d=b/(1ll<<k),c=((1ll<<k)*(d+1)-1-b)/a+1;if(d&1) return c>r?r+1:c+fun(a,b+c*a,k,r-c);else return c>r?0:fun(a,b+c*a,k,r-c); } signed main() {int T; scanf("%d",&T);while (T--) {int a,b,n;scanf("%lld%lld%lld",&a,&b,&n);int ans=0; b+=a; n--; for (int i=0;i<=54;i++) {int c=n/(1ll<<i+1);ans+=fun(a,b,i,(1ll<<i+1)-1)*c+fun(a,b+(1ll<<i+1)*c*a,i,n-c*(1ll<<i+1));}printf("%lld\n",ans); }return 0; } b.cpp

Problem C?鋼鐵俠的復(fù)仇

設(shè)一個$N\times M$的矩陣,設(shè)$A_{i,j}$表示點$(i,j)$被攻克的難度而$B_{i,j}$ 表示點$(i,j)$被攻克的時間。

從$(i_1,j_1)$點轉(zhuǎn)移到$(i_2,j_2)$點需要花費 $|i_1 - i_2|+|j_1-j_2|$ 的代價。

一條合法的攻克路徑滿足:經(jīng)過路徑上的所有點不重復(fù)而且不能經(jīng)過$A_{i,j} = B_{i,j} = 0$的點。

輸出沿著最長時間路徑訪問的時間。

對于100%的數(shù)據(jù)$ n,m \leq 10^3 $

Sol :本題是一個DP題目。

設(shè)$f_{i,j}$表示經(jīng)過到點$(i,j)$結(jié)尾的路徑的最大時間。

顯然需要按照$A_{i,j}$值遞增的順序依次更新每個點。

轉(zhuǎn)移方程就是$f_{i,j} = \max\limits_{k=1,w=1} ^ {k\leq n , w\leq m} [(A_{k,w} < A_{i,j}) f_{k,w}+|i-k|+|j-w|] + b_{i,j} $

復(fù)雜度 $O(n^2 m^2)$ get 30pts

# pragma GCC optimize(2) # include <bits/stdc++.h> # define int long long using namespace std; const int N=1e3+10; struct rec{int x,y,d; }; int n,m; struct cmp {bool operator () (rec a,rec b) {return a.d>b.d;} }; int a[N][N],b[N][N],f[N][N]; priority_queue<rec,vector<rec>,cmp>q; signed main() {scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)scanf("%d",&a[i][j]),q.push((rec){i,j,a[i][j]});for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)scanf("%d",&b[i][j]);int ans=0;while (!q.empty()) {rec u=q.top();q.pop();if (a[u.x][u.y]==0&&b[u.x][u.y]==0) continue;f[u.x][u.y]=b[u.x][u.y];for (int i=1;i<=n;i++)for (int j=1;j<=m;j++)if (a[i][j]!=0&&a[i][j]<a[u.x][u.y]&&!(i==u.x&&j==u.y)) f[u.x][u.y]=max(f[u.x][u.y],f[i][j]+b[u.x][u.y]+abs(u.x-i)+abs(u.y-j));ans=max(ans,f[u.x][u.y]); } printf("%d\n",ans);return 0; } c_30pts.cpp

考慮優(yōu)化這個轉(zhuǎn)移。

顯然絕對值符號可以被拆,例如$(x,y)$在$(i,j)$左上角的時候($i \geq x , j \geq y$)時 有$f_{i,j} +| i - x | +| j - y | = f_{i,j}+i+j - x - y$

還有其他的三種情況不再贅述。

按照@hjc20032003的方法,可以先按$A_{i,j}$的大小把元素分成若干塊(每一塊里面的所有元素$A_{i,j}$值都相同)。

顯然第k塊中的$f_{i,j}$是k-1那一塊的$f_{i,j}$轉(zhuǎn)移而來。

我們先考慮$(x,y)$在$(i,j)$左上角的情況。

可以把相鄰兩塊和并(由于當(dāng)前行一定是從前一個可能的$A_{i,j}$轉(zhuǎn)移而來的),并按照x坐標(biāo)排序,然后從先到后依次掃,如果當(dāng)前的元素$(x,y)$之前在前一個塊(k-1)中那么插入到線段樹第y號位子中。

如果當(dāng)前元素在當(dāng)前塊$k-1$中那么利用當(dāng)前線段樹的元素查詢在他左上角元素的信息來更新當(dāng)前位置的f值$f_{x,y}$。

由于插入線段樹的元素的$x$的坐標(biāo)都是小于當(dāng)前的元素的$x$坐標(biāo)的,并且我們可以通過查詢當(dāng)前元素$y$之前區(qū)間信息而控制轉(zhuǎn)移來源的點是在當(dāng)前點的左上方的。

當(dāng)然,由于可能從4個不同的方向轉(zhuǎn)移,類似的操作要做4次(x軸翻轉(zhuǎn),y軸翻轉(zhuǎn),o點翻轉(zhuǎn),不翻轉(zhuǎn)) 。 具體可以參考[Violet]天使玩偶/SJY擺棋子?的處理方法。

復(fù)雜度大概是$O(4 \times nm log_2 nm)$??

#include<bits/stdc++.h> #define GX(x,y) x=max(x,y) #define ls(x) x<<1 #define rs(x) x<<1|1 #define A first #define B second #define mk make_pair #define pb push_back #define int long long #define REP(i,s,t) for(int i=s;i<=t;i++) using namespace std; const int maxn=1005,inf=0x3fffffffffffffff; typedef pair<int,int> pii; vector<pii> vec[maxn*maxn]; int n,m,dis[maxn*maxn],a[maxn][maxn],b[maxn][maxn],f[maxn][maxn]; struct rec{int x,y; bool w;}q[maxn*maxn]; bool tag[maxn<<2]; int maxx[maxn<<2]; void push_down(int p){if(tag[p]) tag[ls(p)]=tag[rs(p)]=true,maxx[ls(p)]=maxx[rs(p)]=-inf,tag[p]=false; } void _modify(int p,int l,int r,int tar,int val){if(l==r){GX(maxx[p],val); return;}int m=l+r>>1;push_down(p);if(tar<=m) _modify(ls(p),l,m,tar,val);else _modify(rs(p),m+1,r,tar,val);maxx[p]=max(maxx[ls(p)],maxx[rs(p)]); } void modify(int tar,int val){_modify(1,1,m,tar,val);} int _query(int p,int l,int r,int nl,int nr){if(nl<=l&&r<=nr) return maxx[p];int m=l+r>>1,ret=-inf;push_down(p);if(nl<=m) GX(ret,_query(ls(p),l,m,nl,nr));if(m<nr) GX(ret,_query(rs(p),m+1,r,nl,nr));return ret; } int query(int nl,int nr){return _query(1,1,m,nl,nr);} bool cmp1(rec a,rec b){if(a.x!=b.x) return a.x<b.x;return a.y<b.y; } signed main(){ // freopen("c.in","r",stdin); // freopen("c.out","w",stdout);scanf("%lld%lld",&n,&m);int dis_cnt=0;REP(i,1,n) REP(j,1,m) scanf("%lld",&a[i][j]);REP(i,1,n) REP(j,1,m) scanf("%lld",&b[i][j]);REP(i,1,n) REP(j,1,m) if(a[i][j]) dis[++dis_cnt]=a[i][j];sort(dis+1,dis+1+dis_cnt);int cnt=unique(dis+1,dis+1+dis_cnt)-dis-1;REP(i,1,n) REP(j,1,m) if(a[i][j])a[i][j]=lower_bound(dis+1,dis+cnt+1,a[i][j])-dis,vec[a[i][j]].pb(mk(i,j));for(int i=0;i<vec[1].size();i++) f[vec[1][i].A][vec[1][i].B]=b[vec[1][i].A][vec[1][i].B];REP(c,2,cnt){int pt=0;for(int i=0;i<vec[c-1].size();i++) q[++pt]=(rec){vec[c-1][i].A,vec[c-1][i].B,0};for(int i=0;i<vec[c].size();i++) q[++pt]=(rec){vec[c][i].A,vec[c][i].B,1};sort(q+1,q+1+pt,cmp1);tag[1]=true; maxx[1]=-inf;REP(i,1,pt)if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]-q[i].x-q[i].y);else GX(f[q[i].x][q[i].y],query(1,q[i].y)+b[q[i].x][q[i].y]+q[i].x+q[i].y);tag[1]=true; maxx[1]=-inf;for(int i=pt;i;i--) if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]+q[i].x-q[i].y);else GX(f[q[i].x][q[i].y],query(1,q[i].y)+b[q[i].x][q[i].y]-q[i].x+q[i].y);tag[1]=true; maxx[1]=-inf;REP(i,1,pt) if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]-q[i].x+q[i].y);else GX(f[q[i].x][q[i].y],query(q[i].y,m)+b[q[i].x][q[i].y]+q[i].x-q[i].y);tag[1]=true; maxx[1]=-inf;for(int i=pt;i;i--)if(!q[i].w) modify(q[i].y,f[q[i].x][q[i].y]+q[i].x+q[i].y);else GX(f[q[i].x][q[i].y],query(q[i].y,m)+b[q[i].x][q[i].y]-q[i].x-q[i].y);}int ans=0;REP(i,1,n) REP(j,1,m) GX(ans,f[i][j]);cout<<ans<<endl;return 0; } c.cpp

但是,這樣處理的常數(shù)非常大,由于絕對值符號的性質(zhì),按照上述四種轉(zhuǎn)移方法只可能有一種轉(zhuǎn)移方法是正確的。

由于絕對值的性質(zhì)有$|i-x| \geq i-x$ 所以有

?$?f_{i,j} + |i-x| + |j - y| =? max\{ f_{i,j} + (i - x) + (j - y), f_{i,j} + (i - x) + (y - j), f_{i,j} + (x - i) + (j - y), f_{i,j} + (x - i) + (y - j)\}?$?成立。

所以轉(zhuǎn)移的時候可以直接無視$i,x,j,y$的大小關(guān)系,一并轉(zhuǎn)移即可。

轉(zhuǎn)移方程是??$?f_{x,y}= max\{?-x - y + max(f_{i,j} + i + j),?-x + y + max(f_{i,j}?+ i - j),??x - y + max(f_{i,j}? - i + j),?x + y + max(f_{i,j}?- i - j)?) $?

其中$max(f_{i,j} + i + j)? ... $是全局變量在每次轉(zhuǎn)移完成后維護(hù)即可。

還要注意初始值不能加上坐標(biāo) , 即非0的最小的$A_{i,j}$所對應(yīng)的$f_{i,j}$一開始從自己轉(zhuǎn)移不能加上橫縱坐標(biāo)!!!

# include <bits/stdc++.h> # define int long long # define inf (0x3f3f3f3f3f3f3f3f) using namespace std; const int N=1e3+10; int f[N][N],b[N][N]; vector<pair<int,int> >v[N*N]; pair<int,int>t[N*N]; int mx1,mx2,mx3,mx4,ans,mint; int n,m; int Max(int a,int b,int c,int d) {if (b>a) a=b;if (c>a) a=c;if (d>a) a=d;return a; } void work(int r) {if (v[r].size()==0) return;int cnt=0;for (int i=0;i<v[r].size();i++) t[++cnt]=v[r][i];int mxa=0,mxb=0,mxc=0,mxd=0;for (int i=1;i<=cnt;i++) {int x=t[i].first,y=t[i].second;if (r==0) continue;if (r!=mint) f[x][y]=Max(-x-y+mx1,-x+y+mx2,x-y+mx3,x+y+mx4)+b[x][y];else f[x][y]=b[x][y];ans=max(ans,f[x][y]);mxa=max(mxa,f[x][y]+x+y); mxb=max(mxb,f[x][y]+x-y);mxc=max(mxc,f[x][y]-x+y); mxd=max(mxd,f[x][y]-x-y);}mx1=max(mx1,mxa); mx2=max(mx2,mxb);mx3=max(mx3,mxc); mx4=max(mx4,mxd); } signed main() {scanf("%lld%lld",&n,&m);mint=inf;for (int i=1;i<=n;i++)for (int j=1;j<=m;j++) {int t; scanf("%lld",&t);if (t!=0) mint=min(mint,t);v[t].push_back(make_pair(i,j)); }for (int i=1;i<=n;i++)for (int j=1;j<=m;j++) scanf("%lld",&b[i][j]);for (int a=0;a<=1000000;a++) work(a); printf("%lld\n",ans); return 0; } c.cpp

?

轉(zhuǎn)載于:https://www.cnblogs.com/ljc20020730/p/11146210.html

總結(jié)

以上是生活随笔為你收集整理的HGOI20190707 题解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。