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;elset2=(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;elser=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;elseK[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]]);elseif(op[i]=='<')printf("%lld\n",presum[K[i]-1]);elseprintf("%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 ); }
想不出来也是一种无奈...