good problem-2

1.E1. Cats on the Upgrade (easy version)

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<ll,int>
using namespace std;
const int maxn=5e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
ll n,q,p[maxn],pre[maxn];
int main(){
    n=read();q=read();
    char ch[maxn];cin>>ch;
    stack<int>qq;
    for(int i=1;i<=n;i++){
        if(ch[i-1]=='(')qq.push(i);
        else if(!qq.empty())p[i]=p[qq.top()-1]+1,qq.pop();
        pre[i]=pre[i-1]+p[i];
    }
    for(int i=1;i<=q;i++){
        int t=read(),l=read(),r=read();
        printf("%lld\n",pre[r]-pre[l-1]-(p[r]-p[l-1])*p[l-1]);
    }
    return 0;
}


2.Division
首先把数组中的\(a_i\)变为\(log_2(a_i)\)(也就是除2变为1的次数)
这样这道题就转化为每次操作将长度大于k的区间的数减一,求最小操作次数使得数组全为0

对于这种区间加减的题可以考虑差分思想,把a数组转为差分数组,这样就是求差分数组全为0的最小操作次数
每次区间[l,r]减一可以看作\(a_l-1,a_{r+1}+1\)
那么从前往后扫,遇到一个小于0的\(a_r\),从前面最远\(大于0的a_l (且 k\leq r-l)\)来抵消\(a_r\),这样就实现了[l,r-1]的区间减一,若\(a_l\)不足以抵消,那么l++重复上述过程,如果\(k> r-l\)那么就无法成立,输出-1
这样操作数最小,因为差分之后,每个+都需要一个-来消除,所以操作数是固定的。当你做多余操作比如在该-的地方进行+,才会导致操作数变多

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=1e5+101;
const int MOD=1e9+7;
const int inf=2147483647;
const double pi=acos(-1);
ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int t,n,k,a[maxn];
int main(){
    t=read();
    while(t--){
        n=read();k=read();a[n+1]=a[0]=0;
        for(int i=1;i<=n;i++){
            ll x=read();a[i]=0;
            while(x>1)a[i]++,x>>=1;
        }
        for(int i=n+1;i>0;i--)a[i]-=a[i-1];
        int l=1;bool fa=true;
        vector<pa>ans;ans.clear();
        for(int i=2;i<=n+1;i++){
            if(a[i]<0 && i-l<k){fa=false;break;}
            while(a[i]<0){
                if(i-l<k){fa=false;break;}
                if(-a[i]>=a[l]){
                    a[i]+=a[l++];
                    while(a[l-1]){
                        ans.push_back(make_pair(l-1,i-1));
                        a[l-1]--;
                    }
                }
                else {
                    a[l]=a[l]+a[i];a[i]=-a[i];
                    while(a[i]){
                        ans.push_back(make_pair(l,i-1));
                        a[i]--;
                    }
                }
            }
            if(!fa)break;
        }
        for(int i=1;i<=n+1;i++){
            if(a[i]){fa=false;break;}
        }
        if(!fa)printf("-1\n");
        else {
            printf("%lu\n",ans.size());
            for(auto i: ans)printf("%d %d\n",i.first,i.second);
        }
    }
    return 0;
}


3.gcd
首先题目可以转化为
\(\sum_i \prod_{j=1}^n (\lfloor \frac{r_j}{i} \rfloor -\lfloor \frac{l_j-1}{i} \rfloor)\)
括号里可以考虑整除分块,\((\lfloor \frac{r_j}{i} \rfloor -\lfloor \frac{l_j-1}{i} \rfloor)\)的取值在\(i\in [l,r]\)内是相同的,并且取值的个数是\(\sqrt n\)量级的,
那么可以对于每个j,对于一个取值相同的区间乘以当前的取值(每个j都在ans的数组进行乘操作,这样\(\sum_{i=1}ans_i就是答案\)
首先能够想到线段树进行区间乘法,但是带个log
考虑差分思想,区间[l,r]乘v等同于\(ans_l*v,ans_{r+1}*\frac{1}{v}\)
但是乘以0要单独考虑,用c数组记录乘以0的区间,也同样用差分

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=5e5+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
ll power(ll x,ll y){
    ll ans=1;
    while(y){
        if(y&1)ans=ans*x%MOD;
        y>>=1;x=x*x%MOD;
    }
    return (ans%MOD+MOD)%MOD;
}
int n,book[maxn],c[maxn];
ll ans[maxn],inv[maxn];
struct wzq{int l,r;}a[maxn];
int main(){
    n=read();int maxx=0;
    for(int i=1;i<=n;i++){
        a[i].l=read();a[i].r=read();
        maxx=max(maxx,a[i].r);
    }
    inv[1]=1;
    for(int i=2;i<=300000;i++)inv[i]=(ll)(MOD-MOD/i)*inv[MOD%i]%MOD;
    for(int i=0;i<=maxx;i++)ans[i]=1;
    for(int i=1;i<=n;i++){
        a[i].l--;c[a[i].r+1]++;
        for(int l=1,r,r1,r2;l<=a[i].r;l=r+1){
            int x=a[i].r/l,y=a[i].l/l;
            r1=a[i].r/x;
            if(y)r2=a[i].l/y;
            else r2=a[i].r;
            r=min(r1,r2);
            ll now=(x-y)%MOD;
            if(!now)c[l]++,c[r+1]--;
            else {
                ans[l]=ans[l]*now%MOD;
                ans[r+1]=ans[r+1]*inv[now]%MOD;
            }
        }
    }
    ll sum=0;
    for(int i=1;i<=maxx;i++){
        ans[i]=ans[i]*ans[i-1]%MOD;c[i]+=c[i-1];
        if(!c[i])(sum+=ans[i])%=MOD;
    }
    printf("%lld",(sum%MOD+MOD)%MOD);
    return 0;
}


另一道整出分块题GCD
题解:
如果要判断gcd为a是否能够被选出,那么就需要判断[l,r]能否选出至少k个a的倍数
即r/a-(l-1)/a>=k
我们发现r/a的值可以用整除分块算出,(l-1)/a的值也可以整除分块算出。将算出的这些值用双
指针进行组合,即可得到r/a-(l-1)/a>=k所有可能的取值。

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<ll,int>
using namespace std;
const int maxn=3e5+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
const double eps=1e-12;

ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}

int main(){
	ll l=read()-1,r=read(),k=read(),ans=0;
	for(ll i=1,j;i<=r;i=j+1){
		j=min(r/(r/i),i<=l?l/(l/i):r/(r/i));
		if(r/i-l/i>=k)ans+=j-i+1;
	}
	printf("%lld\n",ans);
    return 0;
}

4.E - Average and Median
平均数:二分平均数mid,把序列全部减去mid,按题目要求用dp求最大值,大于0就说明可以
中位数:二分中位数mid,把序列大于mid的设为1,小于mid的设为-1,按题目要求用dp求最大值,大于0就说明可以

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=5e5+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,a[maxn],b[maxn],c[maxn];
int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i];
    double l=0,r=inf,ans;
//l从0开始不能从1开始,因为若序列都为1则无法得到正确结果
    while(r-l>1E-4){
        vector<double>dp(n+1);
        double mid=(l+r)/2.0;
        for(int i=1;i<=n;i++){
            if(i==1){dp[i]=a[i]-mid;continue;}
            if(i==2){dp[i]=max(0.0,dp[1])+a[i]-mid;continue;}
            dp[i]=max(dp[i-1],dp[i-2])+a[i]-mid;
        }
        if(max(dp[n],dp[n-1])>0)ans=mid,l=mid;
        else r=mid;
    }
    printf("%.4lf\n",ans);
    ans=0;sort(b+1,b+n+1);int tot=0;
    for(int i=1;i<=n;i++){
        if(b[i]!=b[i-1])b[++tot]=b[i];
    }
    int l1=1,r1=tot;
    while(r1>=l1){
        int mid=(l1+r1)>>1,mm=b[mid];
        vector<int>dp(n+1);
        for(int i=1;i<=n;i++){
            int now=((a[i]-mm)<0?-1:1);
            if(i==1){dp[i]=now;continue;}
            if(i==2){dp[i]=max(0,dp[1])+now;continue;}
            dp[i]=max(dp[i-1],dp[i-2])+now;
        }
        if(max(dp[n],dp[n-1])>0)ans=mm,l1=mid+1;
        else r1=mid-1;
    }
    printf("%d\n",(int)(ans));
    return 0;
}

5.F - |LIS| = 3
考虑nlogn的求最大上升子序列的做法
\(dp_{i,x1,x2,x3}\)为到第i位置,最长上升序列长度为1/2/3的最后一位的最小值为x1/x2/x3
到第i+1位通过枚举当前为的可能的数,来进行转移
注意最长上升长度不能超过3

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=2e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,m;
ll dp[1001][12][12][12];
int main(){
    n=read();m=read();
    dp[0][m+1][m+1][m+1]=1;
    //m+1就记为当前没有该长度的最长上升子序列
    for(int i=0;i<n;i++)for(int x1=1;x1<=m+1;x1++)for(int x2=1;x2<=m+1;x2++)for(int x3=1;x3<=m+1;x3++){
        if(!dp[i][x1][x2][x3])continue;
        for(int j=1;j<=m;j++){
            if(j<=x1)(dp[i+1][j][x2][x3]+=dp[i][x1][x2][x3])%=MOD;
            else if(j<=x2)(dp[i+1][x1][j][x3]+=dp[i][x1][x2][x3])%=MOD;
            else if(j<=x3)(dp[i+1][x1][x2][j]+=dp[i][x1][x2][x3])%=MOD;
        }
    }
    ll ans=0;
    for(int x1=1;x1<=m;x1++)for(int x2=1;x2<=m;x2++)for(int x3=1;x3<=m;x3++)(ans+=dp[n][x1][x2][x3])%=MOD;
    printf("%lld",ans);
    return 0;
}

6.F - Two Exams
因为题目中的每个citizen有2个参数,不妨先降维,以第一维从大到小排序获得第二维的数组
题目要求未选中的citizens中不能有比选中的更优的可能
所以设\(dp_{i,j,k}\)为前i个(排完序的第二维数组中)数中选j个,其中剩下i-j个citizens中的最小值

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=2e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,K;
ll dp[302][302][302];
struct wzq{int fir,sec;}a[302];
bool cmp(wzq i,wzq j){return i.fir<j.fir;}
int main(){
    n=read();K=read();
    for(int i=1;i<=n;i++)a[i].fir=read();
    for(int i=1;i<=n;i++)a[i].sec=read();
    sort(a+1,a+n+1,cmp);dp[0][0][n+1]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=min(i,K);j++){
            for(int k=1;k<=n+1;k++){
                if(!dp[i-1][j][k])continue;
                (dp[i][j][min(a[i].sec,k)]+=dp[i-1][j][k])%=MOD;   //不选
                if(k>a[i].sec)(dp[i][j+1][k]+=dp[i-1][j][k])%=MOD; //选
            }
        }
    }
    ll ans=0;
    for(int k=1;k<=n;k++)(ans+=dp[n][K][k])%=MOD;
    if(K==n)ans=dp[n][K][n+1];     //如果K==n,全都选
    printf("%lld",(ans%MOD+MOD)%MOD);
    return 0;
}

7.[SDOI2009]HH的项链
显然这种查询区间种类的操作是不能用普通线段树的区间操作
考虑每个项链的贡献
因为是离线操作,可以先固定r,那么[l,r]区间的贡献就是从后往前不同项链的个数
那么我们可以对于每个位置i,记录pre[i]为上一个颜色的位置,随着r的推移,add(r,1),add(pre[r],-1)
这样就能进行区间查询

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=1e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,m,sum[maxn],pre[maxn],book[maxn];
struct wzq{
    int l,r,id;
}q[maxn];
bool cmp(wzq i,wzq j){
    return i.r<j.r;
}
bool cmp1(wzq i,wzq j){
    return i.id<j.id;
}
int lowbit(int x){return x&(-x);}
void add(int l,int val){
    if(!l)return ;
    for(int i=l;i<=n;i+=lowbit(i))sum[i]+=val;
}
int query(int x){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))ans+=sum[i];
    return ans;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        int x=read();
        pre[i]=book[x];
        book[x]=i;
    }
    m=read();
    for(int i=1;i<=m;i++){q[i].l=read();q[i].r=read();q[i].id=i;}
    sort(q+1,q+m+1,cmp);
    int r=0;
    for(int i=1;i<=m;i++){
        while(r<q[i].r){
            r++;
            add(r,1);
            add(pre[r],-1);
        }
        q[i].l=query(q[i].r)-query(q[i].l-1);
    }
    sort(q+1,q+m+1,cmp1);
    for(int i=1;i<=m;i++)printf("%d\n",q[i].l);
    return 0;
}

[HEOI2012]采花
这道题也是同理,只是倒着做

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=2e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,c,m,sum[maxn],bk[maxn],book[maxn];
struct wzq{
    int l,r,id;
}q[maxn];
bool cmp(wzq i,wzq j){
    return i.l>j.l;
}
bool cmp1(wzq i,wzq j){
    return i.id<j.id;
}
int lowbit(int x){return x&(-x);}
void add(int l,int val){
    if(!l)return ;
    for(int i=l;i<=n;i+=lowbit(i))sum[i]+=val;
}
int query(int x){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))ans+=sum[i];
    return ans;
}
int a[maxn];
int main(){
    n=read();c=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=n;i;i--){
        bk[i]=book[a[i]];
        book[a[i]]=i;
    }
    for(int i=1;i<=m;i++){q[i].l=read();q[i].r=read();q[i].id=i;}
    sort(q+1,q+m+1,cmp);
    int l=n+1;
    for(int i=1;i<=m;i++){
        while(l>q[i].l){
            l--;
            add(bk[l],1);
            add(bk[bk[l]],-1);
        }
        q[i].l=query(q[i].r)-query(q[i].l-1);
    }
    sort(q+1,q+m+1,cmp1);
    for(int i=1;i<=m;i++)printf("%d\n",q[i].l);
    return 0;
}

8.little w and Discretization
每一个离散化的操作可以看成是一个区间求mex的操作,得到区间的 mex 之后,在询问这个区间比mex 大的数有多少个就是每一个询问的答案了。
首先求区间mex ,这个可以用线段树来做,线段树维护区间编号出现的最左的位置,叶子节点维护节点编号最后出现的位置。
求完mex,剩下的就简单了,询问这个区间大mex 大的数有多少个,经典题了,用权值线段树即可。

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=3e5+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,a[maxn],m;
struct wzq{int l,r,mex,id;}q[maxn];
struct Tree{        //权值线段树
    int cnt,rt[maxn];
    Tree(){cnt=0;}
    struct inf{
        int lr,rc,v;
        inf(){v=0;}
    }tre[maxn<<5];
    void build(int &k,int l,int r){
        k=++cnt;
        if(l==r)return ;
        int mid=(l+r)>>1;
        build(tre[k].lr,l,mid);build(tre[k].rc,mid+1,r);
        return ;
    }
    void modify(int &now,int pre,int l,int r,int pos){
        tre[++cnt]=tre[pre];now=cnt;tre[cnt].v++;
        if(l==r)return ;
        int mid=(l+r)>>1;
        if(pos<=mid)modify(tre[now].lr,tre[pre].lr,l,mid,pos);
        else modify(tre[now].rc,tre[pre].rc,mid+1,r,pos);
        return ;
    }
    int query(int x,int y,int l,int r,int pos){
        if(r<=pos)return tre[x].v-tre[y].v;
        if(l>pos)return 0;
        int mid=(l+r)>>1;
        return query(tre[x].lr,tre[y].lr,l,mid,pos)+query(tre[x].rc,tre[y].rc,mid+1,r,pos);
    }
}T2;
struct MEX{         // 求MEX
    struct ww{
        int v;
        ww(){v=-1;}
    }tr[maxn<<2];
    void change(int k,int l,int r,int val,int pos){
        if(l==r){tr[k].v=pos;return ;}
        int mid=(l+r)>>1;
        if(val<=mid)change(k<<1,l,mid,val,pos);
        else if(val>mid)change(k<<1|1,mid+1,r,val,pos);
        tr[k].v=min(tr[k<<1].v,tr[k<<1|1].v);
    }
    int bi_query(int k,int l,int r,int pos){
        if(l==r)return l;
        int mid=(l+r)>>1;
        if(tr[k<<1].v<pos)return bi_query(k<<1,l,mid,pos);
        else return bi_query(k<<1|1,mid+1,r,pos);
    }
}T1;
int main(){
    int maxx=0;
    n=read();for(int i=1;i<=n;i++)a[i]=read();
    m=read();for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].id=i;
    sort(q+1,q+m+1,[](wzq i,wzq j){return i.r<j.r;});
    for(int i=1;i<=m;i++){
        if(q[i].r!=q[i-1].r){
            for(int j=q[i-1].r+1;j<=q[i].r;j++){
                if(a[j]<=n)T1.change(1,1,n+1,a[j],j);
            }
        }
        q[i].mex=T1.bi_query(1,1,n+1,q[i].l);
    }
    T2.build(T2.rt[0],1,n+1);
    for(int i=1;i<=m;i++){
        if(q[i].r!=q[i-1].r){
            for(int j=q[i-1].r+1;j<=q[i].r;j++){
                if(a[j]>n+1)T2.rt[j]=T2.rt[j-1];
                else T2.modify(T2.rt[j],T2.rt[j-1],1,n+2,a[j]);
            }
        }
        q[i].mex=q[i].r-q[i].l+1-T2.query(T2.rt[q[i].r],T2.rt[q[i].l-1],1,n+2,q[i].mex-1);
    }
    sort(q+1,q+m+1,[](wzq i,wzq j){return i.id<j.id;});
    for(int i=1;i<=m;i++)printf("%d\n",q[i].mex);
    return 0;
}

9.F - Sum Sum Max
简单来说,是正的就加,不是要么全加来考虑后续加减,要么不加
对所有可能取max

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=2e6+101;
const int MOD=998244353;
const ll inf=2147483647;
const double pi=acos(-1);
ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
ll t,n,m;
ll x[maxn],y[maxn];
int main(){
    t=read();
    while(t--){
        n=read();m=read();
        for(int i=1;i<=n;i++)x[i]=read(),y[i]=read();
        ll a=0,b=0,ans=-1E18;
        for(int i=1;i<=n;i++){
            ans=max(ans,b+a+x[i]);
            if(x[i]<=0 && a+x[i]>=0){
                ll l=0,r=y[i],ji=0;
                while(r>=l){
                    ll mid=(l+r)>>1;
                    if(a+x[i]*mid>=0)l=mid+1,ji=mid;
                    else r=mid-1;
                }
                ans=max(ans,b+(a+x[i]+a+ji*x[i])*ji/2);
            }
            b=b+a*y[i]+1ll*x[i]*y[i]*(y[i]+1)/2;
            ans=max(ans,b);
            a=a+x[i]*y[i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}

10.D - Sequence Query
multiset是库中一个非常有用的类型,它可以看成一个序列,插入一个数,删除一个数都能够在O(logn)的时间内完成,而且他能时刻保证序列中的数是有序的,而且序列中可以存在重复的数。
set和multiset的区别是后者可以存在重复的数

点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<queue>
#include<set>
#define ll long long 
using namespace std;
const int maxn=200000+101;
const int MOD=998244353;
const int inf=2147483647;
ll read(){
	ll x=0,f=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
	for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
	return x*f;
}
int q;
int main(){
	q=read();
	multiset<ll>s;
	s.insert(1000000000000000000+1);
	while(q--){
		int opt=read();
		ll x=read();
		if(opt==1){
			s.insert(x);
		}
		else if(opt==2){
			int k=read();
			if(s.empty()){
				printf("-1\n");continue;
			}
			multiset<ll>::iterator it;
			it=s.upper_bound(x);  //第一个大于x的数
			bool fa=false;
			while(k--){
				if(it==s.begin()){fa=!fa;break;}
				it--;
			}
			if(!fa)printf("%lld\n",*it);
			else printf("-1\n");
		}
		else {
			int k=read();
			if(s.empty()){printf("-1\n");continue;}
			multiset<ll>::iterator it;
			it=s.lower_bound(x);	//第一个大于等于x的数
			bool fa=false;
			k--;
			while(k--){
				if(it==s.end() || *it==1000000000000000000+1){fa=!fa;break;}
				it++;
			}
			if((it==s.end() || *it==1000000000000000000+1)&& !fa){fa=!fa;}
			if(!fa)printf("%lld\n",*it);
			else printf("-1\n");
		}
	}

	return 0;
}
posted @ 2022-01-18 20:13  I_N_V  阅读(37)  评论(0编辑  收藏  举报