雅禮集訓 2017 Day1 解題報告
阿新 • • 發佈:2018-12-29
「雅禮集訓 2017 Day1」市場
挺神仙的一題。涉及區間加、區間除、區間最小值和區間和。雖然標算就是暴力,但是複雜度是有保證的。
我們知道如果線段樹上的一個結點,\(max=min\) 或者 \(max=min+1\) 並且 \(d|max\),是可以直接剪掉的。
我們定義線段樹上一個結點的勢能為 \(\log(max-min)\),那麼我們每執行一次區間除,都會引起勢能的減小。
但是執行區間加時我們涉及 \(\log n\) 個結點,最差情況下會將它們的勢能恢復為 \(\log(max-min)\)
所以總時間複雜度就是勢能總和,不難分析為 \(O(n\log d+q\log n\log d)\)
類似的,我們可以分析區間開根區間加的時間複雜度,在此就不贅述了。
\(Code\ Below:\)
#include <cstdio> #include <iostream> #define int long long #define lson (rt<<1) #define rson (rt<<1|1) using namespace std; const int maxn=100000+10; const int inf=1e18; int n,m,a[maxn],sum[maxn<<2],Max[maxn<<2],Min[maxn<<2],add[maxn<<2]; inline int read(){ register int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return (f==1)?x:-x; } inline void pushup(int rt){ sum[rt]=sum[lson]+sum[rson]; Max[rt]=max(Max[lson],Max[rson]); Min[rt]=min(Min[lson],Min[rson]); } inline void pushtag(int rt,int C,int len){ sum[rt]+=C*len; Max[rt]+=C;Min[rt]+=C;add[rt]+=C; } inline void pushdown(int rt,int len){ if(add[rt]){ pushtag(lson,add[rt],len-(len>>1)); pushtag(rson,add[rt],len>>1); add[rt]=0; } } void build(int l,int r,int rt){ if(l == r){ sum[rt]=Max[rt]=Min[rt]=a[l]; return ; } int mid=(l+r)>>1; build(l,mid,lson); build(mid+1,r,rson); pushup(rt); } void update_plu(int L,int R,int C,int l,int r,int rt){ if(L <= l && r <= R){ sum[rt]+=(r-l+1)*C; Max[rt]+=C;Min[rt]+=C;add[rt]+=C; return ; } pushdown(rt,r-l+1); int mid=(l+r)>>1; if(L <= mid) update_plu(L,R,C,l,mid,lson); if(R > mid) update_plu(L,R,C,mid+1,r,rson); pushup(rt); } void update_div(int L,int R,int C,int l,int r,int rt){ if(L <= l && r <= R){ int A,B; if(Max[rt]<0) A=(Max[rt]-C+1)/C; else A=Max[rt]/C; if(Min[rt]<0) B=(Min[rt]-C+1)/C; else B=Min[rt]/C; if(Max[rt]-A==Min[rt]-B){ pushtag(rt,A-Max[rt],r-l+1); return ; } } pushdown(rt,r-l+1); int mid=(l+r)>>1; if(L <= mid) update_div(L,R,C,l,mid,lson); if(R > mid) update_div(L,R,C,mid+1,r,rson); pushup(rt); } int query_min(int L,int R,int l,int r,int rt){ if(L <= l && r <= R){ return Min[rt]; } pushdown(rt,r-l+1); int mid=(l+r)>>1,ans=inf; if(L <= mid) ans=min(ans,query_min(L,R,l,mid,lson)); if(R > mid) ans=min(ans,query_min(L,R,mid+1,r,rson)); return ans; } int query_sum(int L,int R,int l,int r,int rt){ if(L <= l && r <= R){ return sum[rt]; } pushdown(rt,r-l+1); int mid=(l+r)>>1,ans=0; if(L <= mid) ans+=query_sum(L,R,l,mid,lson); if(R > mid) ans+=query_sum(L,R,mid+1,r,rson); return ans; } signed main() { n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(); build(1,n,1); int op,l,r,k; for(int i=1;i<=m;i++){ op=read(),l=read(),r=read();l++;r++; if(op==1) k=read(),update_plu(l,r,k,1,n,1); if(op==2) k=read(),update_div(l,r,k,1,n,1); if(op==3) printf("%lld\n",query_min(l,r,1,n,1)); if(op==4) printf("%lld\n",query_sum(l,r,1,n,1)); } return 0; }
「雅禮集訓 2017 Day1」矩陣
構造題。
發現無解的情況就是全空的情況,特判掉即可。
現在考慮怎麼算出最少步數。
發現只要第 \(i\) 行有黑色,那麼第 \(i\) 列會在 \(tot_{white}\) 步變成全黑,所以我們來構造一行全黑的,然後將整個棋盤染成黑色。
那麼每行取個最小值即可,累加一下。
\(Code\ Below:\)
#include <bits/stdc++.h> using namespace std; const int maxn=1000+10; int n,m,h[maxn],l[maxn],ans,sum; char s[maxn][maxn]; int main() { scanf("%d%d",&n,&m); int flag=0; for(int i=1;i<=n;i++){ scanf("%s",s[i]+1); for(int j=1;j<=n;j++) if(s[i][j]=='#'){ flag=1; h[i]++;l[j]++; } } if(!flag){ printf("-1\n"); return 0; } ans=n; for(int i=1;i<=n;i++) ans=min(ans,n-h[i]+!l[i]); for(int i=1;i<=n;i++) sum+=l[i]!=n; printf("%d\n",ans+sum); return 0; }