校内集训20180918
$T1:$(Loj507)
$N$张扑克牌依次添加,花色为$a_i$,点数为$b_i$,每添加一张牌$i$可以选择另一张花色相同的牌$j$并把$j,j+1,\ldots,i$的牌移走,产生这些牌点数之和的贡献,问最多
产生多少贡献。
$N\leq 2\times 10^6$,时限$600ms$。
题解:
$O(N^2)DP$不是重点,重点是如何优化$dp[i]=max(dp[i],dp[j-1]+sum[i]-sum[j-1]|a_j==a_i)$这个方程。
可以发现$dp[i]=sum[i]+max\{dp[j-1]-sum[j-1]|aj==ai\}$,那么我们考虑对每个颜色维护当前最大的$dp[j-1]-sum[j-1]$,即可$O(1)$转移。
傻逼题$100pts$没有挂。
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define MAXN 1000005 #define MAXM 500005 #define INF 1LL<<50 #define ll long long ll dp[MAXN],c[MAXN],v[MAXN]; ll maxn[MAXN],sum[MAXN]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } int main(){ ll N=read(),K=read(); for(ll i=1;i<=N;i++) c[i]=read(); for(ll i=1;i<=K;i++) maxn[i]=-INF; for(ll i=1;i<=N;i++) v[i]=read(),sum[i]=sum[i-1]+v[i]; for(ll i=1;i<=N;i++){ dp[i]=max(dp[i-1],sum[i]+maxn[c[i]]); /*for(ll j=1;j<i;j++) if(c[j]==c[i]){ dp[i]=max(dp[i],sum[i]+dp[j-1]-sum[j-1]); }*/ maxn[c[i]]=max(maxn[c[i]],dp[i-1]-sum[i-1]); } printf("%lld\n",dp[N]); return 0; } /* 18 5 5 2 3 5 1 3 5 2 1 4 2 4 5 4 1 1 1 5 8 2 7 6 10 8 10 9 10 2 4 7 7 7 7 9 7 3 */
$T2:$(Loj2279)
给定$N$个年份和该年的降水量,再给定$Q$个问题,每个问题由$(Y,X)$表示,判断“$X$年是$Y$年以来降水量最多的”这句话是否正确。
注意$Y\sim X$之间的降水量不一定是给定的,如果不能判断是否正确则输出不确定。
$N\leq 50000$,空间限制$64MB$。
题解:
如果没有不确定这个选项那么就是一个$ST$表模板。
然而现在要写巨大的一个分类讨论。
理论上来说只需要在确定的基础上判断若$Y\sim X$中间有任意一点未知则不确定。
然而我好像写丑了。
码农题$0pts$喜闻乐见。
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define MAXN 50005 #define MAXM 500005 #define INF 0x7fffffff #define ll long long int year[MAXN],level[MAXN]; int N,f[MAXN][17],log2[MAXN]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } void init(){ log2[1]=0; for(int i=2;i<=MAXN;i++) log2[i]=log2[i>>1]+1; for(int i=1;i<=N;i++) f[i][0]=level[i]; for(int j=1;j<17;j++) for(int i=1;i+(1<<j-1)<=N;i++) f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]); return; } int main(){ //freopen("1 (1).in","r",stdin); //freopen("2.ans","w",stdout); N=read(); for(int i=1;i<=N;i++) year[i]=read(),level[i]=read(); init(); int M=read(); for(int i=1;i<=M;i++){ int Y=read(),X=read(); if(Y>=X){ cout<<"false"<<endl; continue; } int t1=lower_bound(year+1,year+1+N,Y)-year; int t2=lower_bound(year+1,year+1+N,X)-year; //cout<<t1<<":"<<t2<<endl; if(year[t1]!=Y && year[t2]!=X){ cout<<"maybe"<<endl; continue; } else if(year[t1]!=Y && year[t2]==X){ if(t1==t2){ cout<<"maybe"<<endl; continue; } else{ t2--;int k=log2[t2-t1+1]; int maxn=max(f[t1][k],f[t2-(1<<k)+1][k]); if(maxn>=level[t2+1]) cout<<"false"<<endl; else cout<<"maybe"<<endl; continue; } } else if(year[t2]!=X && year[t1]==Y){ if(t1+1==t2){ cout<<"maybe"<<endl; continue; } else{ t2--;t1++;int k=log2[t2-t1+1]; int maxn=max(f[t1][k],f[t2-(1<<k)+1][k]); if(maxn>=level[t1-1]) cout<<"false"<<endl; else cout<<"maybe"<<endl; continue; } } else{ if(t1+1==t2){ if(level[t2]<=level[t1]){ if(t2-t1+1==X-Y+1) cout<<"true"<<endl; else cout<<"maybe"<<endl; } else cout<<"false"<<endl; continue; } else{ t2--;t1++;int k=log2[t2-t1+1]; int maxn=max(f[t1][k],f[t2-(1<<k)+1][k]); if(maxn>=level[t1-1] || maxn>=level[t2+1] || level[t2+1]>level[t1-1]) cout<<"false"<<endl; else if(t2-t1+3==X-Y+1) cout<<"true"<<endl; else cout<<"maybe"<<endl; continue; } } } return 0; }
$T3:$(Loj2332)
给你$i$个数,若$A_i\geq A_{i-1}$则$T_i=-(A_i-A_{i-1})\times S$,否则$T_i=-(A_i-A_{i-1})\times T$,有$M$组更新$(L,R)$将$[L,R]$区间内的$A_i\rightarrow A_{i+v}$。
每次更新后输出$ans=\sum T_i$的值。
$N\leq 2\times 10^5$,时限$200ms$。
题解:
考场上一直在想怎么把线段树优化过时限。于是$0pts$。
后来考完一想,$T$每次更新只需要更俩数,$ans$只需要把这更新的俩数值加上再减去原值就行了。
在线求助智力康复学校。
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define MAXN 200005 #define MAXM 500005 #define INF 0x7fffffff #define ll long long ll N,Q,S,T,A[MAXN],B[MAXN]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } ll calc(ll x){return x>0?-x*S:-x*T;} int main(){ N=read(),Q=read(),S=read(),T=read(); ll ans=0; for(ll i=0;i<=N;i++){ A[i]=read();if(!i) continue; B[i]=A[i]-A[i-1];ans+=calc(B[i]); } //cout<<sum[N+1]<<endl; while(Q--){ ll l=read(),r=read(),v=read(); ans-=calc(B[l]);B[l]+=v;ans+=calc(B[l]); if(r!=N) ans-=calc(B[r+1]),B[r+1]-=v,ans+=calc(B[r+1]); printf("%lld\n",ans); } return 0; }
总结:
三道“思维”题$100pts$收场喜闻乐见。
正如$Lv$神所言:“这样去考$noip$还不如回家洗洗睡”。
的确像这种题目都不能保证$100%AC$还拿什么$noip$高分,不如去搞文化课了。
模拟考试的时候手懒,$noip$的时候可能就一直懒下去了吧。
后面几场模拟考试要认真打了,不然在这耗$3h$有什么意义呢。
共勉。