20171001四校聯考
1.積木大賽
(block.pas/c/cpp)
【問題描述】
為了慶祝國慶,廈門一中舉辦了一年一度的“積木大賽”。
在2013年NOIP大賽中,夏夏同學己經搭建了寬度為n的大廈,其中第i塊高度為hi。今年比賽的內容是對其NOIP2013搭建大廈進行擴建,使用的材料也都是體積為1正方體積木。
今年搭建的規則是:如果要在某一個位置上放一個積木,必須滿足它的左下、下方、右下都有積木(用二維坐標a表示,如果要在a[i,j]位置放積木,那麽a[i-1,j-1]、a[i,j-1]、a[i+1,j-1]必須要有積木)。
如果搭的積木大廈越高,夏夏同學就會覺得越有成就感,現有m個積木,問你能搭建的最大高度是多少?
【輸入】
第一行兩個用空格隔開的整數n和m,分別表示己搭好的寬度和可以使用的積木數量。
後面有n行,每行一個整數hi表示己搭建的第i列積木的高度。
【輸出】
一個整數,表示能搭建的最大高度。
【輸入輸出樣例】
樣例1 |
樣例2 |
||
block.in |
block.out |
block.in |
block.out |
8 4 3 4 2 1 3 3 2 4 |
5 |
3 100 3 3 3 |
4 |
【數據說明】
30%的數據滿足:n<=10;m<=1000。
50%的數據滿足:n<=100;m<=1000,000。
70%的數據滿足:n<=1000;m<=10,000,000。
80%的數據滿足:n<=10,000;m<=100,000,000。
100%的數據滿足:n<=100,000;m<=1000,000,000;1<=hi<=100000。
二分答案+雙指針掃描
二分搭建的高度,最低為maxh+1,最高為maxh+sqrt(m)+1
如何在O(n)時間內check呢?
對於搭建積木,我們要搭出一個金字塔形,但是,並不是要搭建整個金字塔形,有時候只要搭建部分即可,如圖:
我們僅需搭建綠色部分,而不需要搭建藍色方框內的整個金字塔
如何找到綠色部分呢?
設l為綠色部分的左邊界,r為綠色部分的右邊界,x為當前要搭建的金字塔頂,h為大金字塔塔高
我們需要在搭建的金字塔所需積木=整個大金字塔所需積木-黃色部分所需積木-紫色部分所需積木-棕色部分所需積木
設黃色部分高為a,則a=h-(x-l)(即圖中6-1=5),黃色部分所需積木為a*(a+1)/2。
同理,能算出紫色部分所需積木。
棕色部分所需積木=r前面所有已有積木-l前面所有已有積木。我們想到了什麽?前綴和
這樣,綠色部分所需積木就算出來了,我們比較它與m的大小關系即可
等等!還沒說l和r怎麽求呢?
我們發現,對於一個位置x,如果能找到它所對的l,那麽對於位置x-1,它的l必≤位置x所對的l,且從位置x所對的l ~x-1絕對不滿足能做綠色部分的左邊框。因為在向左移的過程中,左邊的每個位置的h的要求總是增大的(金字塔形)
所以l具有單調性。我們從n到1掃描每個位置,如果當前的l不滿足h[l]<h-(i-l)(即不能做綠色部分的左邊框),那麽我們l--,直到滿足要求,然後我們拿一個數組記下每個位置的l
對r也是一樣,只要從左往右掃描即可。
註意:前綴和要開long long,二分答案的下限一定要從maxh+1開始(不然會引發l==r==i或l>i或r<i等奇奇怪怪的事故),l和r不能小於1或大於n(超出給定範圍n外的位置不能放積木)
#include<iostream> #include<cstdio> #include<cmath> using namespace std; typedef long long ll; int h[100001],Max=0; long long bb[100001];int n,m; int L[100001],R[100001]; bool check(int x) { int l=n,r=1; for(int i=n;i>=1;i--){while(h[l]<x-(i-l)&&l>=1)l--;L[i]=l;} for(int i=1;i<=n;i++){while(h[r]<x-(r-i)&&r<=n)r++;R[i]=r;} for(int i=1;i<=n;i++) { if(R[i]==n+1||L[i]==0)continue; int a=x-(i-L[i]),b=x-(R[i]-i); ll y=(ll)x*(ll)x-(ll)(b+1)*(ll)b/2ll-(ll)(a+1)*(ll)a/2ll; if(y-(ll)(bb[R[i]-1]-bb[L[i]])<=(ll)m)return true; } return false; } int main() { // freopen("block.in","r",stdin);freopen("block.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&h[i]);Max=max(Max,h[i]);bb[i]=bb[i-1]+(ll)h[i]; } int l=Max+1,r=(int)(sqrt(m)+1.00)+Max+1; while(l<r) { int mid=(l+r)/2; if(check(mid))l=mid+1; else r=mid; } cout<<l-1; return 0; }View Code
2.愛心月餅
(cake.pas/c/cpp)
【問題描述】
中秋節馬上就要到了,電腦組的女生們準備自己動手做月餅給大家分享,烘培老師告訴她們做月餅需要面粉和餡,女孩子們網上各種搜索,采購到了M種面粉和N種口味不同的餡,其中第i種面粉有mi斤,第i種餡有ni斤。這裏的種類從0開始標號。
經過實踐,女孩子們發現每份月餅需要1斤面粉和1斤餡,為了讓收到禮物的同學感到很特別,她們決定不讓兩份月餅用同一種面粉和同一種餡做成(即每種月餅之間面粉和餡的種類至少有一個不同),女孩子們現在就想知道她們采購到的這些材料可以做多少份愛心月餅?這種簡單的問題就交給你啦,她們還得趕著去做月餅呢。
【輸入】
由於輸入很大,所以使用一種方法生成輸入。
l m0= m0
l mi+1 = (mi* 58+md) mod (N + 1)
l n0 = n0
l ni+1 = (ni *58 + nd) mod (M + 1)
第一行6個整數為M,N,m0,md,n0,nd
【輸出】
輸出一行,為最多能做月餅的份數。
【輸入輸出樣例】
對於10%的操作,N,M ≤ 5.
對於30%的操作,N,M ≤ 1000.
對於50%的操作,N,M ≤ 10 5 .
對於100%的操作,1 ≤ N,M ≤ 2.5 ? 10^ 6 , 0 ≤ m0,md ≤ N,0 ≤ n0, nd ≤ M.
【數據說明】
cake.in |
cake.out |
2 3 1 3 1 0 |
2 |
5 8 1 2 3 4 |
19 |
貪心,對於每種面粉,肯定與最大的mi種餡配對。
3.直徑
(tree/tree.in/tree.out)
【題目大意】
收到禮物的同學們用愛心月餅擺出了一棵有n個點的樹,邊有長度。然後得意地給其他同學出了一個問題:對於每一條邊,把這條邊刪掉之後得到的兩棵樹的直徑是多少?
【輸入文件】
第一行一個正整數n,表示樹的點數
接下來的n-1行每行有三個數u,v,w,表示有一根樹枝連接u和v,長度為w
【輸出文件】
對於第i條邊(從1開始標號),你將會得到2個答案Pi和Qi,我們令ansi=max(Pi,Qi)*23333+min(Pi,Qi)*2333+233*i*i+23*i+2,請輸出一個數S表示所有ansi的和對2333333333333333取模的值
【輸入樣例】
10
1 2 234
2 9 936
9 5 784
5 3 105
2 8 775
8 10 368
10 6 1003
9 4 670
4 7 417
【輸出樣例】
735923484
【數據規模與約定】
對於10%的數據,保證n≤300
對於30%的數據,保證n≤5000
對於50%的數據,保證n≤100000
對於70%的數據,保證n≤1000000
對於100%的數據,保證n≤4000000,w≤1000000
T3喪病題,下面附上solution和std
10分算法:
對於每條刪邊在兩端O(n^2)求樹的直徑
時間復雜度O(n^3),空間復雜度O(n)
30分算法:
對於每條刪邊在兩端O(n)求樹的直徑
時間復雜度O(n^2),空間復雜度O(n)
50分算法:
可以寫個點分治冷靜一下
要是你真的去寫了你就確實應該冷靜一下了
時間復雜度O(nlogn),空間復雜度O(n)
100分算法1(不推薦):
樹形DP
需要先計算一個點向下走的最大值,次大值,第三大值。
然後計算一個點向上走的最大值。
然後計算這個點和它的父親的邊斷掉後這個子樹的答案,這個比較好計算。
最後計算這個點和它的父親的邊斷掉後這個子樹之外的答案,分三種情況:
1、它的父親的其他兒子的向下答案
2、它的父親斷邊之後的答案
3、它的父親向上的答案
這個做法的細節參見tree2.cpp
註意你不能對每個點掃一遍它的父親的其它兒子,一個菊花樹就能卡掉,要維護一個前綴的最值和一個後綴的最值。
該方法常數可能略大
時間復雜度O(n),空間復雜度O(n)
100分算法2:
先求出樹的直徑,然後分刪掉的邊是否在直徑上討論。
刪掉的邊不在直徑上時,其中一端的答案就是直徑,另一端做十分簡單的樹形DP即可。
考慮自左到右計算刪掉的邊在直徑上的左邊的答案(右邊同理),使用類似NOIP2016day2T2的方法即可。
時間復雜度O(n),空間復雜度O(n)
第二種做法
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define Rep(i,x) for(int i=head[x];i+1;i=nxt[i]) #define pb push_back #include<algorithm> using namespace std; typedef long long ll; const int N=4e6+5; const int M=8e6+5; inline void read(int &x){x=0;char ch=getchar(); while(ch<‘0‘) ch=getchar(); while(ch>=‘0‘){x=x*10+ch-48;ch=getchar();}} inline void judge() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); } int fa[N],head[N],nxt[M],to[M],e,w[M],id[M]; int wod[N]; int son[N][2],pd[N],tp[N]; inline void init(){memset(head,-1,sizeof(head)); e=0;} inline void add_edge(int x,int y,int z,int ii){to[e]=y;w[e]=z;nxt[e]=head[x];id[e]=ii;head[x]=e++;} ll ma[N][2],ans[N][2]; inline void Insert(int x,ll y,int cjl,int ii) { if(ma[x][0]<y){ma[x][1]=ma[x][0];ma[x][0]=y;son[x][1]=son[x][0];son[x][0]=cjl;pd[x]=ii;} else if(y>ma[x][1]){ma[x][1]=y;son[x][1]=cjl;} } void dfs(int x) { ma[x][0]=ma[x][1]=0;Rep(i,x) { int j=to[i]; if(j==fa[x]) continue; fa[j]=x; tp[j]=id[i]; dfs(j); Insert(x,ma[j][0]+(ll)w[i],j,tp[j]); } } inline void Insert3(int x,ll y) { if(ma[x][0]<y){ma[x][1]=ma[x][0];ma[x][0]=y;} else if(y>ma[x][1])ma[x][1]=y; } void dfs3(int x,int f,int pp) { ma[x][0]=ma[x][1]=0;ans[pp][0]=0;Rep(i,x) { int j=to[i]; if(j==f) continue; dfs3(j,x,id[i]); Insert3(x,ma[j][0]+(ll)w[i]); ans[pp][0]=max(ans[pp][0],ans[id[i]][0]); }ans[pp][0]=max(ans[pp][0],ma[x][0]+ma[x][1]); } vector<int> tt,Route,Rid,tt2; void dfs2(int x) { tt.push_back(x); if(pd[x])tt2.push_back(pd[x]); if(son[x][0]) dfs2(son[x][0]); else return; } ll md[N]; int main() { judge(); int n;read(n);init();rep(i,1,n-1) { int x,y,z;read(x);read(y);read(z); add_edge(x,y,z,i);add_edge(y,x,z,i); wod[i]=z; }fa[1]=0;dfs(1);int mj=1;rep(i,2,n) if(ma[i][0]+ma[i][1]>ma[mj][0]+ma[mj][1]) mj=i; rep(i,1,n-1) ans[i][1]=ma[mj][0]+ma[mj][1]; if(son[mj][0]) { dfs2(son[mj][0]); for(int i=(int)tt.size()-1;i>=0;i--) Route.pb(tt[i]); for(int i=(int)tt2.size()-1;i>=0;i--) Rid.pb(tt2[i]); tt2.clear();tt.clear(); Rid.pb(tp[son[mj][0]]); } Route.pb(mj); if(son[mj][1]) { Rid.pb(tp[son[mj][1]]); dfs2(son[mj][1]); for(int i=0;i<(int)tt.size();i++) Route.pb(tt[i]); for(int i=0;i<(int)tt2.size();i++) Rid.pb(tt2[i]); } int sz=Route.size();memset(ma,0,sizeof(ma)); ll pre=0;ll maa=0; rep(i,0,sz-1) { int j=Route[i]; Rep(xjt,j) { if(i && to[xjt]==Route[i-1]) continue; if(i<sz-1 && to[xjt]==Route[i+1]) continue; dfs3(to[xjt],j,id[xjt]); md[i]=max(md[i],w[xjt]+ma[to[xjt]][0]); } maa=max(maa,md[i]+pre);if(i!=sz-1)ans[Rid[i]][0]=maa; if(i!=sz-1) pre+=wod[Rid[i]]; } pre=0; maa=0;memset(md,0,sizeof(md)); per(i,sz-1,0) { int j=Route[i]; Rep(xjt,j) { if(i && to[xjt]==Route[i-1]) continue; if(i<sz-1 && to[xjt]==Route[i+1]) continue; dfs3(to[xjt],j,id[xjt]); md[i]=max(md[i],w[xjt]+ma[to[xjt]][0]); } maa=max(maa,md[i]+pre);if(i)ans[Rid[i-1]][1]=maa; if(i) pre+=wod[Rid[i-1]]; } rep(i,1,n-1) if(ans[i][0]>ans[i][1]) swap(ans[i][0],ans[i][1]); ll solo=0; rep(i,1,n-1) { solo+=ans[i][1]*23333ll+ans[i][0]*2333ll+233ll*(ll)i*(ll)i+23ll*(ll)i+2ll; //cerr<<ans[i][1]<<‘ ‘<<ans[i][0]<<endl; solo%=2333333333333333ll; } printf("%lld\n",solo); return 0; }View Code
第一種做法
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<vector> using namespace std; inline void read(int &x) { char ch=getchar();x=0; while ((ch<‘0‘)||(ch>‘9‘)) ch=getchar(); while ((ch>=‘0‘)&&(ch<=‘9‘)) {x=x*10+ch-‘0‘;ch=getchar();} } vector <int> des[4000100],len[4000100]; vector <long long> pre[4000100],suf[4000100]; int s1[4000100],s2[4000100]; int fa[4000100],cop[4000100],wei[4000100]; long long d0[4000100],d1[4000100],d2[4000100],u[4000100],fl[4000100]; int nd0[4000100],nd1[4000100],nd2[4000100]; long long dans[4000100],uans[4000100]; void upd(long long w,int p,int s) { if (w>=d0[s]) { d2[s]=d1[s];nd2[s]=nd1[s]; d1[s]=d0[s];nd1[s]=nd0[s]; d0[s]=w;nd0[s]=p; } else if (w>=d1[s]) { d2[s]=d1[s];nd2[s]=nd1[s]; d1[s]=w;nd1[s]=p; } else if (w>=d2[s]) { d2[s]=w;nd2[s]=p; } } int main() { freopen("cheese.in","r",stdin); freopen("cheese.out","w",stdout); int n;read(n); for (int i=1;i<n;i++) { int u,v,w;read(u);read(v);read(w); s1[i]=u;s2[i]=v; des[u].push_back(v);len[u].push_back(w); des[v].push_back(u);len[v].push_back(w); } int head=1,tail=0;cop[head]=1; while (head!=tail) { int s=cop[++tail]; for (int k=0;k<des[s].size();k++) if (des[s][k]!=fa[s]) { fa[des[s][k]]=s;fl[des[s][k]]=len[s][k];wei[des[s][k]]=k; cop[++head]=des[s][k]; } } for (int i=n;i>=1;i--) { int s=cop[i]; for (int k=0;k<des[s].size();k++) if (des[s][k]!=fa[s]) upd(d0[des[s][k]]+len[s][k],des[s][k],s); } for (int s=1;s<=n;s++) { pre[s].resize(des[s].size()); for (int k=0;k<des[s].size();k++) if (des[s][k]==fa[s]) { if (k) pre[s][k]=pre[s][k-1]; else pre[s][k]=-2100000000; } else { if (k) pre[s][k]=max(pre[s][k-1],d0[des[s][k]]+fl[des[s][k]]); else pre[s][k]=d0[des[s][k]]+fl[des[s][k]]; } suf[s].resize(des[s].size()); for (int k=des[s].size()-1;k>=0;k--) if (des[s][k]==fa[s]) { if (k!=des[s].size()-1) suf[s][k]=suf[s][k+1]; else suf[s][k]=-2100000000; } else { if (k!=des[s].size()-1) suf[s][k]=max(suf[s][k+1],d0[des[s][k]]+fl[des[s][k]]); else suf[s][k]=d0[des[s][k]]+fl[des[s][k]]; } } for (int i=2;i<=n;i++) { int s=cop[i]; if (fa[s]) u[s]=max(u[s],u[fa[s]]+fl[s]); /*for (int k=0;k<des[fa[s]].size();k++) if ((des[fa[s]][k]!=s)&&(des[fa[s]][k]!=fa[fa[s]])) u[s]=max(u[s],d0[des[fa[s]][k]]+fl[des[fa[s]][k]]+fl[s]);*/ if (wei[s]) u[s]=max(u[s],pre[fa[s]][wei[s]-1]+fl[s]); if (wei[s]!=pre[fa[s]].size()-1) u[s]=max(u[s],suf[fa[s]][wei[s]+1]+fl[s]); } for (int i=n;i>=1;i--) { int s=cop[i]; dans[s]=d0[s]+d1[s]; for (int k=0;k<des[s].size();k++) if (des[s][k]!=fa[s]) dans[s]=max(dans[s],dans[des[s][k]]); } for (int s=1;s<=n;s++) { pre[s].resize(des[s].size()); for (int k=0;k<des[s].size();k++) if (des[s][k]==fa[s]) { if (k) pre[s][k]=pre[s][k-1]; else pre[s][k]=-2100000000; } else { if (k) pre[s][k]=max(pre[s][k-1],dans[des[s][k]]); else pre[s][k]=dans[des[s][k]]; } suf[s].resize(des[s].size()); for (int k=des[s].size()-1;k>=0;k--) if (des[s][k]==fa[s]) { if (k!=des[s].size()-1) suf[s][k]=suf[s][k+1]; else suf[s][k]=-2100000000; } else { if (k!=des[s].size()-1) suf[s][k]=max(suf[s][k+1],dans[des[s][k]]); else suf[s][k]=dans[des[s][k]]; } } for (int i=2;i<=n;i++) { int s=cop[i]; uans[s]=uans[fa[s]]; if (nd0[fa[s]]==s) uans[s]=max(uans[s],d1[fa[s]]+max(u[fa[s]],d2[fa[s]])); else if (nd1[fa[s]]==s) uans[s]=max(uans[s],d0[fa[s]]+max(u[fa[s]],d2[fa[s]])); else uans[s]=max(uans[s],d0[fa[s]]+max(u[fa[s]],d1[fa[s]])); /*for (int k=0;k<des[fa[s]].size();k++) if ((des[fa[s]][k]!=fa[fa[s]])&&(des[fa[s]][k]!=s)) uans[s]=max(uans[s],dans[des[fa[s]][k]]);*/ if (wei[s]) uans[s]=max(uans[s],pre[fa[s]][wei[s]-1]); if (wei[s]!=pre[fa[s]].size()-1) uans[s]=max(uans[s],suf[fa[s]][wei[s]+1]); } long long ans=0; for (int i=1;i<n;i++) { if (fa[s2[i]]==s1[i]) swap(s1[i],s2[i]); long long p1=dans[s1[i]],p2=uans[s1[i]]; if (p1<p2) swap(p1,p2); ans+=p1*23333+p2*2333+(long long)i*i*233+(long long)i*23+2; ans%=2333333333333333ll; //cerr<<p1<<‘ ‘<<p2<<endl; } printf("%lld\n",ans); }View Code
20171001四校聯考