17.10.10
- 上午
- 模拟考试(大悲催…)
- Prob.1(WA)
一个套路,没做过真的好难想出来
简化题目:
对于一个整数序列,把任意一个元素修改为任意一个整数为一次操作,问最少需要几次操作可以使得序列(严格)单调上升。
开始想的是求一个LIS(最长上升子序列),然后发现不对,因为只能修改为整数。
然后就想不出来了,555
正解:
把序列的每个元素的值减去该元素的标号,即a[ i ]-=i ;
然后再求一个最长不下降子序列,这样求出来的最长序列可以保证:在该最长不下降子序列中的元素不修改的情况下,其他元素一定可以修改成功。
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int lson[100005],rson[100005],key[100005],t[100005],now[100005]; int n,cnt; void DFS(int u){ if(lson[u]) DFS(lson[u]); ++cnt; t[cnt]=key[u]-cnt; if(rson[u]) DFS(rson[u]); } void LIS(){ cnt=0; for(int i=1;i<=n;i++){ if(!cnt||now[cnt]<=t[i]){ now[++cnt]=t[i]; } else{ int l=upper_bound(now+1,now+cnt+1,t[i])-now; now[l]=t[i]; } } printf("%d",n-cnt); } int main(){ freopen("binary.in","r",stdin); freopen("binary.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&key[i]); for(int i=2,a,b;i<=n;i++){ scanf("%d%d",&a,&b); if(!b) lson[a]=i; else rson[a]=i; } DFS(1); LIS(); return 0; }
- Prob.1(WA)
- 模拟考试(大悲催…)
- Prob.2(AC)
一个小技巧:一个序列区间的gcd如果等于该区间的最小值,那么这个区间的所有数都可以被该最小值整除。
本题两个ST表维护
- Prob.3(WA)
神题,记忆化搜索
(S->P)反向考虑交换操作,如果最后一次交换的是a[i]和a[i+1],那么必然在该次交换之前,a[l]~a[i]的值都小于a[i+1]~a[r]。
所以对于每个成立的a[i],a[i+1],可以把序列分为两段(左一段右一段),然后递归做相同操作。
然后回溯时对当前递归层的答案贡献
=左段的方案数*右段的方案数*C(r-l-1,i-l),这个组合数考虑两边的交换顺序
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int mod=1000000007; 6 int dp[55][55],fac[55],inv[55]; 7 int aa[55],pos[55]; 8 int power(int a,int b) 9 { 10 int val=1; 11 while(b) 12 { 13 if(b&1) val=(1ll*val*a)%mod; 14 a=(1ll*a*a)%mod; b>>=1; 15 } 16 return val; 17 } 18 void pre_fac_inv(int n) 19 { 20 fac[0]=fac[1]=1; 21 for(int i=2;i<=n;i++) fac[i]=(1ll*fac[i-1]*i)%mod; 22 inv[n]=power(fac[n],mod-2); 23 for(int i=n;i>1;i--) inv[i-1]=(1ll*inv[i]*i)%mod; 24 inv[0]=inv[1]; 25 } 26 int C(int m,int n) 27 { 28 return 1ll*fac[m]*inv[m-n]%mod*inv[n]%mod; 29 } 30 int dfs(int l,int r){ 31 if(l>=r) return 1; 32 if(dp[l][r]) return dp[l][r]; 33 int maxx[55],minn[55]; 34 memset(maxx,0,sizeof(maxx)); 35 memset(minn,0x3f,sizeof(minn)); 36 int ret=0,numl,numr,ma,mi; 37 for(int i=l;i<=r;i++){ 38 maxx[i]=aa[i]; 39 if(i!=l) maxx[i]=max(maxx[i],maxx[i-1]); 40 } 41 for(int i=r;i>=l;i--){ 42 minn[i]=min(minn[i],aa[i]); 43 if(i!=r) minn[i]=min(minn[i],minn[i+1]); 44 } 45 for(int i=l;i<r;i++){ 46 swap(aa[i],aa[i+1]); 47 ma=max(maxx[i-1],aa[i]); 48 mi=min(minn[i+2],aa[i+1]); 49 if(ma<mi){ 50 numl=dfs(l,i); 51 numr=dfs(i+1,r); 52 ret=(1ll*ret+1ll*numl*numr%mod*C(r-l-1,i-l)%mod)%mod; 53 } 54 swap(aa[i],aa[i+1]); 55 } 56 return dp[l][r]=ret; 57 } 58 int main(){ 59 freopen("swap.in","r",stdin); 60 freopen("swap.out","w",stdout); 61 int n; 62 scanf("%d",&n); 63 pre_fac_inv(n); 64 for(int i=1;i<=n;i++) scanf("%d",&aa[i]),aa[i]++; 65 int ans=dfs(1,n); 66 printf("%d",ans); 67 return 0; 68 }
- 下午
- 先改了上午的题
- 然后绝望的发现入门OJ上的题做不了了!!!
- 就先把前几天做的12道题的题解贴上。
- 入门OJ 2036 [Noip模拟题]集合贪心
将每个数x化为 x=a*(k^b) (a%k!=0) 的形式
对所有的a排序+离散化
然后排序x后,从小到大去取,
取的条件为:当前数的b,与之前取的a与当前数的a相同的且b值最大的那个数的b 差值不为1
(有点绕,分开描述)
当前数 x=a1*(k^b)
之前的那个数 x'=a1*(k^b'),且那个数在这个a1的前提下,b'为最大
如果 b!=b'+1 则可以取
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; ll a[100005],c[100005],d[100005],tmp; int n,k,m,sub,ans,cnt; int main(){ scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); tmp=a[i]; while(tmp%k==0) tmp/=k; c[++m]=tmp; } sort(a+1,a+n+1); sort(c+1,c+m+1); m=unique(c+1,c+m+1)-c-1; for(int i=1;i<=n;i++){ tmp=a[i]; cnt=0; while(tmp%k==0) tmp/=k,cnt++;cnt++; sub=lower_bound(c+1,c+m+1,tmp)-c; if(!d[sub]||d[sub]!=cnt-1) ans++,d[sub]=cnt; } printf("%d",ans); return 0; }
- 入门OJ 2075 [Noip模拟题]调整
建图:
首先明确一个观点:
如果我们把一些边调整为边权为0,
且计算出起点到终点的最短路值小于等于c
那么一定可以通过让最短路径上调整过的某些边的边权增加,
(即一开始就把不把其边权变为0),使得该路径的权值和恰好为c
然后就建一个类似层次图的东西
第几个层次就表示调整了几条边的权值为0
对于输入的 u,v,w
对每个层次 u->v 建边权为w的边
对每个层次的u->下一个层次的v建一个边权为0的边,经过这条边的话就表示要调整其边权为0
最后从小到大枚举每个层次,若该层次的n号节点的dis<=c,那么该层次就是答案(即调整该层次的序号那么多次)
...不知为何
用优先队列优化过的spfa跑得还没有裸的spfa快代码:
#include<queue> #include<iostream> #include<cstdio> #include<cstring> using namespace std; struct edge{ int to,val,next; }e[3000000]; int head[200000],dis[200000]; bool inq[200000]; int n,m,c,ent=1; void add(int u,int v,int w){ e[ent]=(edge){v,w,head[u]}; head[u]=ent++; } void spfa(){ memset(dis,0x3f,sizeof(dis)); queue<int> q;int u,v; q.push(1);inq[1]=1;dis[1]=0; while(!q.empty()){ u=q.front(); q.pop(); inq[u]=0; for(int i=head[u];i;i=e[i].next){ v=e[i].to; if(dis[v]<=dis[u]+e[i].val) continue; dis[v]=dis[u]+e[i].val; if(inq[v]) continue; q.push(v); inq[v]=1; } } } int main(){ scanf("%d%d%d",&n,&m,&c); for(int i=1,u,v,w;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); for(int j=0;j<=m;j++){ add(u+j*n,v+j*n,w); add(u+j*n,v+(j+1)*n,0); } } spfa(); for(int i=0;i<=m;i++) if(dis[n+i*n]<=c){ printf("%d",i); break; } return 0; }
- 入门OJ 2036 [Noip模拟题]集合贪心
- 入门OJ 2076 [Noip模拟题]集合
因为A,B的差值不大于10^6,所以不存在两个数可以靠大于10^6的素数合并
筛出10^6以内的素数,在筛的同时,用并查集合并。代码
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; ll MAXN=1000000; ll prime[1000006],fa[2000006]; bool np[1000006]; ll ff,ans,A,B,P,cnt; ll find(ll x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void find_and_work(){ for(int i=1;i<=B-A+1;i++) fa[i]=i; for(ll i=2;i<=MAXN;i++){ if(!np[i]){ prime[++cnt]=i; if(i>=P){ ff=0; for(ll j=((A-1)/i+1)*i;j<=B;j+=i){ int f=find(j-A+1); if(!ff) ff=f; else fa[f]=ff; } } } for(ll j=1;j<=cnt&&prime[j]<=MAXN/i;j++){ np[i*prime[j]]=1; if(i%prime[j]==0) break; } } } int main(){ scanf("%lld%lld%lld",&A,&B,&P); MAXN=min(MAXN,B); find_and_work(); for(ll i=1;i<=B-A+1;i++) if(find(i)==i) ans++; printf("%lld",ans); return 0; }
- 入门OJ 2077 [Noip模拟题]购买
按c排序后模拟即可
代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; struct goods{ ll k,c; bool operator <(const goods &rtm) const{ return c<rtm.c; } }g[105]; ll n,b=1,m,ans,t; int main(){ scanf("%lld",&n); for(ll i=1;i<=n;i++) scanf("%lld%lld",&g[i].k,&g[i].c); sort(g+1,g+n+1); scanf("%lld",&t); ll i=1; for(ll j=1,x=0,y=0,z=0;j<=t;j++){ scanf("%lld",&x);z=y; y=x; x=x-z; while(x&&i<=n){ while(g[i].k<=x&&i<=n){ ans+=g[i].k*g[i].c*j; x-=g[i].k; i++; } if(i<=n){ ans+=x*g[i].c*j; g[i].k-=x; x=0; } } } while(i<=n){ ans+=g[i].k*g[i].c*(t+1); i++; } printf("%lld",ans); return 0; }
- 入门OJ 2078 [Noip模拟题]填充棋盘
看题解吧(看不到了诶)
神奇组合题。代码
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int mod=1000000007; int n,m,ans; int pow(int a,int b){ int val=1; while(b){ if(b&1) val=1ll*val*a%mod; a=1ll*a*a%mod; b>>=1; } return val; } int main(){ scanf("%d%d",&n,&m); ans=(1ll*6*(1ll*pow(2,n)+1ll*pow(2,m))-24)%mod; printf("%d",ans); return 0; }
- 入门OJ 2081 [Noip模拟题]画山
dp(背包+递推) 好题
定义dp[i][j]表示终点在(i,j)的方法数,
然后每次枚举线段,从前面转移过来:
dp[i][j]+=dp[i-a],[j-b]
显然有重复呢。
为了去重,把斜率加入dp状态,
dp[i][j][k]:表示终点在(i,j)时,且到达改点用的是斜率编号为k的线段。
目的是每次转移时不能从相同的k转移
此时,就不能类同上面一样枚举每个线段来转移,(否则会少记录方案)
而是应该先预处理,把相同斜率的线段归为一组,然后对每一组跑一个物品无限的背包,
计算出该斜率下可以延伸多长
然后dp时,枚举每一组斜率,然后枚举背包跑出的可以到达的距离,然后转移。O(N*N*M*P)
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; const ll mod=1000000007; struct segment{ int a,b; bool operator <(const segment &rtm) const{ return b*rtm.a<rtm.b*a; } }seg[105]; struct group{ int a,b; bool can[105]; }gro[105]; ll dp[105][105][105],s[105][105]; int gnt,n,m,p; int main(){ scanf("%d%d%d",&n,&m,&p); for(int i=1;i<=p;i++) scanf("%d%d",&seg[i].a,&seg[i].b); sort(seg+1,seg+p+1); int st=1; bool *f; for(int I=1;I<=p+1;I++){ if(I==p+1||(seg[I].b*seg[st].a!=seg[I].a*seg[st].b)){ ++gnt; gro[gnt].a=seg[st].a; gro[gnt].b=seg[st].b; f=gro[gnt].can; f[0]=1; for(int i=st;i<I;i++) for(int j=seg[i].a;j<=n;j++) f[j]=f[j]|f[j-seg[i].a]; st=I; } } s[0][0]=1; for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) for(int k=1;k<=gnt;k++){ for(int o=1;o<=n;o++) if(gro[k].can[o]){ int ii=o,jj=ii*gro[k].b/gro[k].a; if(i-ii<0||j-jj<0||j-jj>m) break; dp[i][j][k]=(dp[i][j][k]+s[i-ii][j-jj]-dp[i-ii][j-jj][k]+mod)%mod; } s[i][j]=(s[i][j]+dp[i][j][k]+mod)%mod; } printf("%lld",s[n][0]); return 0; }
- 入门OJ 2082 [Noip模拟题]益智游戏
益智好题
可以明确的是两个人如果会走到相同的一些点,那么这些点一定是连续的
(都是走最短路嘛)
对于每个人,求出他的最短路DAG图(最短路不止一条啦)
怎么求这个对短路DAG图呢?
对起点和终点分别跑一次最短路,得到两个dis数组(dis1是从起点跑得,dis2是从终点跑的)
对于一条边的 u v w,若dis1[u]+dis2[v]+w==dis(起点->终点),那么这条边就在最短路DAG中
然后呢,取出两个人的最短路DAG图中都走的边构成一个新图
对新图按按拓扑序进行最长路dp。
特判两个人的最短路DAG图是否有相交,
相交的话,答案要加一,因为dp值是最长路,而要求的是点数
不相交的话,就不用加了,因为ans本来就等于0;代码:
#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define INF 0x3f3f3f3f3f3f3f3f #define ll long long #define node(a,b) (node){a,b} using namespace std; struct node{ ll id,d; bool operator <(const node &rtm) const{ return d>rtm.d; } }; struct edge{ ll from,to,val,next; }e1[200005],e2[200005],e3[200005]; ll head1[50005],head2[50005],head3[50005]; ll dis1[50005],dis2[50005],dis3[50005],in[50005]; bool fg[50005],ve[200005],xj; ll n,m,A,B,C,D,ent1=1,ent2=1,ent3=1,disab,discd,ans; void add(ll u,ll v,ll w,ll *head,edge *e,ll &ent){ e[ent]=(edge){u,v,w,head[u]}; head[u]=ent++; } ll dijistra(ll st,ll ed,ll *head,ll *dis,edge *e){ static bool vis[50005]; memset(vis,0,sizeof(bool)*(n+3)); memset(dis,0x3f,sizeof(ll)*(n+3)); priority_queue<node>q; node now; ll u,v; dis[st]=0; q.push(node(st,0)); while(!q.empty()){ now=q.top(); q.pop(); u=now.id; if(vis[u]) continue; vis[u]=1; for(ll i=head[u];i;i=e[i].next){ v=e[i].to; if(vis[v]||dis[v]<=dis[u]+e[i].val) continue; dis[v]=dis[u]+e[i].val; q.push(node(v,dis[v])); } } return dis[ed]; } void topo(ll st,ll ed,ll *head,ll *dis,edge *e){ queue<int>q; for(int i=1;i<=n;i++) if(!in[i]) q.push(i); while(!q.empty()){ int u=q.front(); q.pop(); ans=max(ans,dis[u]); for(int i=head[u];i;i=e[i].next){ int v=e[i].to; dis[v]=max(dis[v],dis[u]+1); in[v]--; if(!in[v])q.push(v); } } } int main(){ //freopen("in.in","r",stdin); scanf("%lld%lld",&n,&m); for(ll i=1,a,b,c;i<=m;i++){ scanf("%lld%lld%lld",&a,&b,&c); add(a,b,c,head1,e1,ent1); add(b,a,c,head2,e2,ent2); } scanf("%lld%lld%lld%lld",&A,&B,&C,&D); disab=dijistra(A,B,head1,dis1,e1); disab=dijistra(B,A,head2,dis2,e2); if(disab==INF){printf("-1");return 0;} for(int i=1;i<ent1;i++){ int u=e1[i].from,v=e1[i].to,w=e1[i].val; if(dis1[u]+dis2[v]+w==disab) fg[u]=fg[v]=1,ve[i]=1; } discd=dijistra(C,D,head1,dis1,e1); discd=dijistra(D,C,head2,dis2,e2); if(discd==INF){printf("-1");return 0;} for(int i=1;i<ent1;i++){ int u=e1[i].from,v=e1[i].to,w=e1[i].val; if(dis1[u]+dis2[v]+w==discd){ if(fg[u]||fg[v]) xj=1; if(!ve[i]) continue; add(u,v,w,head3,e3,ent3); in[v]++; } } topo(C,D,head3,dis3,e3); if(xj) ans++; printf("%lld",ans); return 0; }
- 入门OJ 2083 [Noip模拟题]围篱笆
和上题一样,也是一道"益智"题
代码:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> using namespace std; const double pi=acos(-1); int N; int main(){ while(1){ scanf("%d",&N); if(!N) break; printf("%.2lf\n",1.0*N*N/pi/2); } return 0; }
- 入门OJ 2084 [Noip模拟题]点
假题,其实选的点可以和原来的点重合。
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<windows.h> #define happy_check(); \ int RESULT=MessageBox(NULL,TEXT("Are you happy?"),"Just click on it" ,MB_YESNO);if(RESULT==IDNO){\ MessageBox(NULL,TEXT("Poor guy!\nHope you can be better."),"You are not happy?" ,MB_OK);return 0;} using namespace std; int x[10005],y[10005]; int n,lx,ly,cnt,xx,yy,ans; int abs(int a){ return a<0?-a:a; } int main(){ //happy_check(); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]); sort(x+1,x+n+1); sort(y+1,y+n+1); if(n&1) cnt=1,xx=x[n/2+1],yy=y[n/2+1]; else{ xx=x[n/2]; lx=x[n/2+1]-x[n/2]+1; yy=y[n/2]; ly=y[n/2+1]-y[n/2]+1; cnt=lx*ly; } for(int i=1;i<=n;i++) ans+=abs(xx-x[i]); for(int i=1;i<=n;i++) ans+=abs(yy-y[i]); printf("%d %d",ans,cnt); return 0; }
- 入门OJ 2085 [Noip模拟题]身高
对于每个约束(a,b)
那么a,b之间的应该都比a和b小,所以就差分使得该区间整体小一
注意有重复的(a,b),要去重,否则WA(怎么有一个输入的东西没用上?)
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct cons{ int a,b; bool operator <(const cons &rtm) const{ return a!=rtm.a?a<rtm.a:b<rtm.b; } }t[100005]; int dif[100005]; int N,I,H,R; int main(){ scanf("%d%d%d%d",&N,&I,&H,&R); for(int i=1;i<=R;i++){ scanf("%d%d",&t[i].a,&t[i].b); if(t[i].a>t[i].b) swap(t[i].a,t[i].b); } sort(t+1,t+R+1); for(int i=1,j=0;i<=R;i++){ if(t[i].a==t[j].a&&t[i].b==t[j].b) continue; dif[t[i].a+1]-=1; dif[t[i].b]+=1; j=i; } for(int i=1;i<=N;i++){ H+=dif[i]; printf("%d\n",H); } return 0; }
- 入门OJ 2087 [Noip模拟题]lucky
裸的数位Dp
输入的数不超过10^20,可能爆long long(包括unsigned long long)
所以用字符串读入
(一直wa,最后才发现读入出错了)代码:
#include<cstdio> #include<cstring> #include<iostream> #define ll long long using namespace std; ll dp[30][12][2]; ll N,tail,num[30]; ll dfs(int p,int last,bool ach,bool up){ if(p>tail) return ach; if(!up&&dp[p][last][ach]!=-1) return dp[p][last][ach]; ll ret=0; int r=up?num[p]:9; for(int i=0;i<=r;i++) ret+=dfs(p+1,i,ach||(last==4&&i==9),up&&i==r); if(!up) return dp[p][last][ach]=ret; return ret; } int main(){ num[++tail]=getchar()-'0'; while(num[tail]<0||num[tail]>9) num[tail]=getchar()-'0'; while(num[tail]<=9&&num[tail]>=0) num[++tail]=getchar()-'0'; tail--; memset(dp,-1,sizeof(dp)); cout<<dfs(1,0,0,1); return 0; }
- 入门OJ 2088 [Noip模拟题]小球
模拟,做了超过100000次就停下来
代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; int A,B,cnt; int main(){ scanf("%d%d",&A,&B); while(1){ if(A>B) swap(A,B); if(!A) break; B-=A; A+=A; cnt++; if(cnt>100000){ printf("-1"); return 0; } } printf("%d",cnt); return 0; }
- 晚上
- BZOJ 1011 [HNOI2008]遥远的行星
扯题。
利用题目给的答案误差(大佬博客 ↓)
http://www.cnblogs.com/Sunnie69/p/5575626.html代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 100005 using namespace std; const double eps=1e-8; double m[MAXN],s[MAXN]; double A,ans;int n,p,B; int main(){ scanf("%d%lf",&n,&A); for(int i=1;i<=n;i++) scanf("%lf",&m[i]),s[i]=s[i-1]+m[i]; p=min(2000,n); for(int i=1;i<=p;i++){ ans=0; B=(int)(A*i+eps); for(int j=1;j<=B;j++) ans+=m[j]/(1.0*i-j); printf("%.6lf\n",ans*m[i]); } for(int i=p+1;i<=n;i++){ B=(int)(A*i+eps); ans=m[i]*s[B]/(1.0*i-1.0*B/2); printf("%.6lf\n",ans); } return 0; }
- BZOJ 1013 [JSOI2008]球形空间产生器sphere
设出中心的多维坐标
由给出的n+1个点得出n个等式,
用高斯消元计算出线性方程的各个元的值
至于高斯消元,自己模拟就好了。代码:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; double k[15][15],w[2][15],d[15]; int n; void Gaussian(int p){ if(p>n) return; double b,val=0; for(int r=p+1;r<=n;r++){ b=k[r][p]/k[p][p]; for(int i=p;i<=n+1;i++) k[r][i]=k[r][i]-k[p][i]*b; } Gaussian(p+1); for(int i=p+1;i<=n;i++) val+=d[i]*k[p][i]; d[p]=(k[p][n+1]-val)/k[p][p]; } int main(){ int cur=0; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%lf",&w[cur][i]); for(int r=1;r<=n;r++){ cur^=1; for(int i=1;i<=n;i++)scanf("%lf",&w[cur][i]); for(int i=1;i<=n;i++){ k[r][i]=2*(w[cur^1][i]-w[cur][i]); k[r][n+1]=k[r][n+1]+w[cur^1][i]*w[cur^1][i]-w[cur][i]*w[cur][i]; } } Gaussian(1); for(int i=1;i<n;i++) printf("%.3lf ",d[i]); printf("%.3lf",d[n]); return 0; }
- BZOJ 1012 [JSOI2008]最大数maxnumber
可以线段树。
注意到如果向队尾加入一个元素,
且它前面的元素比它小的话,那么无论怎么询问,它前面的元素都不可能贡献答案
所以维护一个单调栈,有了单调性后,二分寻找答案。代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int num[200005],pos[200005]; int N,D,t,cnt,top,l; int main(){ scanf("%d%d",&N,&D); char com; int x; for(int i=1;i<=N;i++){ scanf(" %c%d",&com,&x); if(com=='A'){ ++cnt; x=(x+t)%D; while(top&&num[top]<=x) top--; num[++top]=x; pos[top]=cnt; } else if(com=='Q'){ x=cnt-x+1; l=lower_bound(pos+1,pos+top+1,x)-pos; t=num[l]; printf("%d\n",t); } } return 0; }
- BZOJ 1011 [HNOI2008]遥远的行星
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas