10.13 上午 考试
T1
直接二分就好了
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; ll n; int a,b,d; ll check(ll x) { ll t1,t2; t1=(ll)(x-a-1)/d+1; if(x>b) t2=(ll)(x-b-1)/d+1; else t2=(ll)(b-x-1)/d+1; return t1+t2; } ll work1() { ll ans=max(a,b),l=max(a,b),r=((ll)1<<60),mid,temp; while(l<=r) { mid=(l+r)>>1; temp=check(mid); if(temp<=n&&ans<mid) ans=mid; if(l>=r) break; if(temp<=n) l=mid+1; else r=mid-1; } return ans; } int main(){ //freopen("T1.in","r",stdin); scanf("%lld%d%d%d",&n,&d,&a,&b); --n; cout<<work1(); }
T2
预处理出来每个点
$L_i$ i左边第一个比它大的点的位置
$R_i$ i右边第一个大于等于它的位置(这样是为了统计的时候不重复)
那么K==$a_i$的区间个数就是$$\sum_{a_i==K}(i-L_i)(R_i-i)$$
然后求一下前缀和和后缀和就行了
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; inline int read() { char q=getchar();int ans=0; while(q<'0'||q>'9')q=getchar(); while(q>='0'&&q<='9'){ans=ans*10+q-'0';q=getchar();} return ans; } inline char readchar() { char q=getchar(); while(q!='<'&&q!='>'&&q!='=')q=getchar(); return q; } const int N=100006; struct JI { int ff,pos,val; bool friend operator < (JI a,JI b) { return a.val<b.val; } }ji[N*3]; int ccc; int now; int n,Q; int a[N],K[N]; char op[N]; void lisan() { now=0; sort(ji+1,ji+1+ccc); for(int i=1;i<=ccc;++i) { if(ji[i].val!=ji[i-1].val) ++now; if(ji[i].ff==1) a[ji[i].pos]=now; else K[ji[i].pos]=now; } } int L[N],R[N]; ll num[N*3],presum[N*3],behsum[N*3]; int zhan[N*2],he; void work() { a[0]=a[n+1]=0x7fffffff; he=0;zhan[++he]=0; for(int i=1;i<=n;++i) { while(a[zhan[he]]<=a[i])--he; L[i]=zhan[he]; zhan[++he]=i; } he=0;zhan[++he]=n+1; for(int i=n;i>=1;--i) { while(a[zhan[he]]<a[i])--he; R[i]=zhan[he]; zhan[++he]=i; } for(int i=1;i<=n;++i) num[a[i]]+=(ll)(i-L[i])*(R[i]-i); for(int i=1;i<=now;++i) presum[i]=presum[i-1]+num[i]; for(int i=now;i>=1;--i) behsum[i]=behsum[i+1]+num[i]; for(int i=1;i<=Q;++i) { if(op[i]=='=') printf("%lld\n",num[K[i]]); else if(op[i]=='<') printf("%lld\n",presum[K[i]-1]); else printf("%lld\n",behsum[K[i]+1]); } } int main(){ freopen("T2.in","r",stdin); n=read();Q=read(); for(int i=1;i<=n;++i) { ++ccc; ji[ccc].val=read(); ji[ccc].ff=1; ji[ccc].pos=i; } for(int i=1;i<=Q;++i) { op[i]=readchar(); ++ccc; ji[ccc].ff=2; ji[ccc].val=read(); ji[ccc].pos=i; } lisan(); work(); }
T3
$f_i$ 所有排列长度为 i 排完序所需要的总步数
那么 $$f_i=i*f_{i-1}+(2^{i-1}-1)*fac_{i-1}$$
我们考虑第 i 位都是谁
是i时,直接加上$f_{i-1}$
1时,加上$f_{i-1}+2^0*fac_{i-1}$
...
然后求和就可以 $O(n)$ 了
解释一下转移:
fac就是阶乘,即长度为 i-1 的排列个数
每次在长度i-1的后面添加一个数x (当然,前i-1个数里可能有i)
那把x扔到第x位需要$2^{x-1}$次(i需要0次)
证明:
比如 3 1 2 弄成 1 2 3 需要3次
4 1 2 3 弄成 1 2 4 3 也需要3次
因为 4可以看成3 再把 3扔到开头到有序,又相当于重复了一遍3 1 2 到 1 2 3 的过程
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <cstdlib> #include <algorithm> #define ll long long #define dd double #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; const int N=100006; const int mod=1e9+7; ll qpow(ll a,int ci) { ll ans=1; while(ci) { if(ci&1) ans=ans*a%mod; a=a*a%mod; ci>>=1; } return ans; } ll jie[N],jieni[N]; void chu() { jie[0]=1; for(int i=1;i<N;++i) jie[i]=jie[i-1]*i%mod; jieni[N-1]=qpow(jie[N-1],mod-2); for(int i=N-2;i>=1;--i) jieni[i]=jieni[i+1]*(ll)(i+1)%mod; jieni[0]=1; } int n; ll f[N],mi[N]; int main(){ chu(); scanf("%d",&n); mi[0]=1; for(int i=1;i<=n;++i) { mi[i]=mi[i-1]*2%mod; f[i]=f[i-1]*i%mod+(mi[i-1]-1+mod)%mod*jie[i-1]%mod; } printf("%lld", f[n]%mod*jieni[n]%mod ); }
想不出来也是一种无奈...