T1 爬山
二分最高高度,$O(1)$判断是否可行。
#include<iostream> #include<cstdio> #define ll long long using namespace std; ll n,d,a,b,ans; ll read() { ll aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } bool check(ll x) { ll ha=(x-a)/d,hb=(x-b)/d; if(d*ha<x-a) ha++; if(d*hb<x-b) hb++; if(ha+hb<=n-1) return true; return false; } int main() { n=read();d=read();a=read();b=read(); if(n==2){ printf("%lld\n",max(a,b)); return 0; } ll l=max(a,b),r=d*(n-1),mid; while(l<=r){ mid=(l+r)>>1; if(check(mid)){ ans=mid; l=mid+1; } else r=mid-1; } printf("%lld\n",ans); return 0; }
T2 学数数
先离散化,用单调栈维护以一个数$x$为最大值,向左到哪,向右到哪,左$×$右就是一共有多少个区间以$x$为最大值。
用前缀和维护以$x$为最大值的区间有多少个,询问的时候$lower_bound$二分位置直接查询。
对于后$40%$的数据中,存在重复的数,那么找左右边界的时候左闭右开(左开右闭也可以),就可以避免这种情况。
记得开$long long$
#include<iostream> #include<cstdio> #include<algorithm> #include<map> #include<queue> #include<stack> #include<set> #define ll long long using namespace std; struct node { ll x,id,l,r; }h[100100]; ll n,Q,t[100100],p[100100]; stack<node>s; set<ll>st; map<ll,ll>mp; char ch[5]; ll read() { ll aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } bool cmp1(node a,node b) { return a.x<b.x; } bool cmp2(node a,node b) { return a.id<b.id; } int main() { //freopen("test.in","r",stdin); //freopen("4.out","w",stdout); n=read();Q=read(); for(int i=1;i<=n;i++){ h[i].x=read(); h[i].id=i; st.insert(h[i].x); } ll num=0; while(st.size()){ mp[*st.begin()]=++num; p[num]=*st.begin(); st.erase(*st.begin()); } sort(h+1,h+n+1,cmp1); for(int i=1;i<=n;i++) h[i].x=mp[h[i].x]; sort(h+1,h+n+1,cmp2); for(int i=1;i<=n;i++){ ll x=h[i].x; while(s.size()&&s.top().x<=x) s.pop(); if(s.size()) h[i].l=s.top().id+1; else h[i].l=1; s.push(h[i]); } while(s.size()) s.pop(); for(int i=n;i>=1;i--){ ll x=h[i].x; while(s.size()&&s.top().x<x) s.pop(); if(s.size()) h[i].r=s.top().id; else h[i].r=n+1; s.push(h[i]); } sort(h+1,h+n+1,cmp1); for(int i=1;i<=n;i++){ t[h[i].x]=t[h[i-1].x]+(h[i].id-h[i].l+1)*(h[i].r-h[i].id); } ll x; for(int i=1;i<=Q;i++){ scanf("%s",ch);x=read(); ll pos=lower_bound(p+1,p+num+1,x)-p; if(ch[0]=='>'){ if(p[pos]==x) printf("%lld\n",t[num]-t[pos]); else printf("%lld\n",t[num]-t[pos-1]); } if(ch[0]=='='){ if(p[pos]==x) printf("%lld\n",t[pos]-t[pos-1]); else puts("0"); } if(ch[0]=='<'){ if(p[pos]==x) printf("%lld\n",t[pos-1]); else printf("%lld\n",t[pos-1]); } } return 0; }
T3 七十和十七
设$f[i]$为长度为$i$的序列(不是排列)中计数器的大小。假设现在已知$f[i-1]$
那么第$i$位与前面的相对大小关系一共有i种
1.第$i$位相对大小为i,对计数器没有任何影响,直接转移 $f[i]=f[i-1]$
2.第$i$位相对大小小于$i$ (即$1~i-1$),设为$j$,那么计数器的值就需要增加$2^{j-1}$ (可以拿几个数试一下),对于前$i-1$个数,他们的顺序有$(i-1)!$种,所以$ f[i]=f[i-1]+2^{j-1}*(i-1)!$
综上,
$f[i]=f[i-1]+(f[i-1]+2^{1-1}*(i-1)!)+(f[i-1]+2^{2-1}*(i-1)!)+(f[i-1]+2^{3-1}*(i-1)!)...+(f[i-1]+2^{i-1-1}*(i-1)!)$
发现$2$的多少次方是一个等比数列,可以直接用前$i-1$项和公式求出来,剩下$i$个$f[i-1]$相加
最终得到$f[i]=f[i-1]*i+(2^{i-1}-1)*(i-1)!$
答案即为$f[n]/n!$
#include<iostream> #include<cstdio> #define mod 1000000007 #define ll long long using namespace std; ll n,ans,f[100100]; ll quick(ll x,ll p) { ll as=1; while(p){ if(p&1) as=as*x%mod; x=x*x%mod; p>>=1; } return as; } int main() { scanf("%lld",&n); ll sum=1,tot=1; for(ll i=1;i<=n;i++){ f[i]=(f[i-1]*i%mod+(tot-1)*sum%mod)%mod; tot=tot*2%mod; sum=sum*i%mod; } printf("%lld\n",f[n]*quick(sum,mod-2)%mod); return 0; }