洛谷2018寒假集訓tg第二次比賽第二題Princess Principal題解
這算不算泄題啊。。。被kkk發現會咕咕咕吧。
題目大意:給定一個數列a,與常數n,m,k然後有m個詢問,每個詢問給定l,r。問在a[l]到a[r]中最少分成幾段,使每段的和不超過k,如果無解,輸出Chtholly
樣例:
input:
5 5 7
2 3 2 3 4
3 3
4 4
5 5
1 5
2 4
output:
1
1
1
2
2
解答:
首先觀察數據範圍,n<=1e+6 可能的復雜度為O(mlogn).暴力能搞30分吧。
其實這題還是很妙的。我們先考慮暴力:對於[L,R],設個res,對[l,r]從左往右掃描,將res累加,當res大於k,ans++;當掃描到一個數大於k輸出無解
我們可以發現這題是基於貪心的思想,使一個連續子段和盡量大。然後就有點難想了。
您覺得,這道題與樹有關系嗎?
還真有關系!!記得樹的父親表示法嗎?對於n個節點,只要一個序列an即可表示這棵樹。
先不考慮無解
對於a[x]我們設ax加到a(y-1)<=k 且ax加到a(y)>k 那麽,我們將y當作x的父親這樣就可以弄一顆樹
舉個栗子:
樣例:2 3 2 3 4
k=7 a4是a1的父親
a4是a2的父親
a5是a2的父親
同時設一個root
root是a4的父親
root是a5的父親
我們發現,一個點從一個節點轉移的另一個節點,等價於在原數列上直接跳過它的最有連續子段。
樹上父親節點的元素編號總是大於兒子
問題轉化成:給定樹上兩點
求較低的點向上走到點p使p的元素編號大於頂一個點需要走幾條邊。
純模擬跟暴力復雜度一樣O(mn)
其實可以用倍增優化,有點類似於倍增求lca我們設anc[u][i]為u上方2^i個節點
有anc[u][i]=anc[anc[u][i-1]][i-1]
然後再加一個dis數組來存每個點的深度,答案就是一個點跳完後與跳之前的dis之差——差分思想
然後我們再來看如何判無解:
無解是由一種情況有a[i]>k(l<=i<=r)產生的
我們再建樹時如果發現a[p]大於k那麽就舍棄這個點
最後的樹不止有一顆,是個森林。
第一次寫博客,不懂可在下方提問。。。。
code:
// luogu-judger-enable-o2 #include <cstdio> #include<algorithm> using namespace std; #define maxn 1000010 struct edge{int to,nxt;}e[maxn<<1]; bool jg[maxn]; int fa[maxn],gf[maxn],n,m,k,a[maxn],dis[maxn],anc[maxn][21],head[maxn],pos; void add(int u,int v){e[++pos]=(edge){v,head[u]},head[u]=pos;e[++pos]=(edge){u,head[v]},head[v]=pos;} int ab(int x){return x>0 ? x:-x;} void dfs(int u,int ft){ int i; for(i=head[u];i;i=e[i].nxt){ int v=e[i].to;if(v==ft)continue; dis[v]=dis[u]+1; gf[v]=gf[u]; fa[v]=u; anc[v][0]=u;dfs(v,u); } } void init(){ int j,i; for(i=1;i<=n;++i)anc[i][1]=anc[anc[i][0]][0]; for(j=2;j<=20;++j) for(i=1;i<=n;++i)anc[i][j]=anc[anc[i][j-1]][j-1]; } int slove(int x,int y){ int ans=1,i;if(x>y)swap(x,y); for(i=20;i>=0;--i) if(anc[x][i]<=y && anc[x][i]) ans+=dis[x]-dis[anc[x][i]],x=anc[x][i]; return ans; } int main(){//freopen("in.txt","r",stdin);freopen("o1.txt","w",stdout); scanf("%d%d%d",&n,&m,&k);int i,j; for(i=1;i<=n;++i)scanf("%d",&a[i]);a[n+1]=1<<30; for(i=1;i<n;++i){ long long res=0;if(a[i]>k){ jg[i]=1;continue; } for(j=i;j<=n+1;++j){ if(a[j]>k){ --j;break; } res+=a[j];if(res>k){ --j;break; } } add(i,j+1); }add(n,n+1); for(i=n+1;i>0;--i) if(!gf[i]){ gf[i]=i;dfs(i,-1); } init(); while(m--){ int x,y;scanf("%d%d",&x,&y); if(gf[x]!=gf[y] || jg[x] || jg[y])printf("Chtholly\n"); else { printf("%d\n",slove(x,y)); } } } /* 5 1 10 2 5 5 2 5 1 5 */
洛谷2018寒假集訓tg第二次比賽第二題Princess Principal題解