传送门

\(A\)

找到能达到的最大的和最小的,那么中间任意一个都可以被表示出来

typedef long long ll;
int n,a,b;ll res;
int main(){
    scanf("%d%d%d",&n,&a,&b);
    if(a>b||n==1&&a!=b)return puts("0"),0;
    res=(a+b+1ll*(n-2)*b)-(a+b+1ll*(n-2)*a)+1;
    printf("%lld\n",res);
    return 0;
}

\(B\)

分别算出需要走一次的对数和两次的对数即可

typedef long long ll;
const int N=1e5+5;
char s[N];int n;ll res,sum;
int main(){
    scanf("%s",s+1),n=strlen(s+1);
    fp(i,1,n)sum+=(s[i]=='U'?n-i:i-1);
    res=(1ll*n*(n-1)-sum)*2+sum;
    printf("%lld\n",res);
    return 0;
}

\(C\)

不难发现蓝点构成了一个类似树的东西,那么连通块个数就是总点数减去边数,分别维护一下就好了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2005;
char mp[N][N];int r[N][N],c[N][N],s[N][N],n,m,q;
inline int qs(R int x,R int y,R int xx,R int yy){
    return s[xx][yy]-s[x-1][yy]-s[xx][y-1]+s[x-1][y-1];
}
inline int qr(R int x,R int y,R int xx,R int yy){
    return r[xx][yy]-r[x-1][yy]-r[xx][y-1]+r[x-1][y-1];
}
inline int qc(R int x,R int y,R int xx,R int yy){
    return c[xx][yy]-c[x-1][yy]-c[xx][y-1]+c[x-1][y-1];
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    fp(i,1,n){
        scanf("%s",mp[i]+1);
        fp(j,1,m)s[i][j]=(mp[i][j]=='1')+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    }
    fp(i,1,n)fp(j,1,m-1){
        r[i][j]=r[i-1][j]+r[i][j-1]-r[i-1][j-1]+(mp[i][j]=='1'&&mp[i][j+1]=='1');
    }
    fp(i,1,n-1)fp(j,1,m){
        c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1]+(mp[i][j]=='1'&&mp[i+1][j]=='1');
    }
    for(R int x,y,xx,yy;q;--q){
        scanf("%d%d%d%d",&x,&y,&xx,&yy);
        printf("%d\n",qs(x,y,xx,yy)-qr(x,y,xx,yy-1)-qc(x,y,xx-1,yy));
    }
    return 0;
}

\(D\)

首先如果\(A=B\)答案为\(1\),所以我们假设\(B>A\)

找到第一个\(A,B\)二进制意义下不同的位\(r\),那么前面的位都是没有用的可以删掉

然后我们把数分成两个集合\(X:[A,2^r),Y:[2^r,B]\)

\(k\)\(B\)中第一个小于\(r\)且为\(1\)的二进制位,则有

  • 只用\(X\)能表示出\([A,2^r)\)(直接选对应的数),且只能表示出这些(能表示出的最小的数为\(A\),最大的数为\(2^r-1\)

  • 只用\(Y\)能且只能表示出\([2^r,2^r+2^{k+1}-1]\)(因为每一个\(2^r+2^i\)(i\leq k)都必定存在于\(Y\)中)

  • 同时用\(X\)\(Y\)能表示出\([2^r+A,2^{r+1}-1]\)(直接选\(2^r\)和一个\(X\)中的数),且只能表示出这些(同上)

那么我们直接加上\([A,2^{r+1}-1]\),然后判一下\(2^r+2^{k+1}-1\)\(2^r+A\)的大小就好了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
ll a,b,res;
int main(){
    scanf("%lld%lld",&a,&b);
    if(a==b)return puts("1"),0;
    R int r,k;
    for(r=59;(a>>r&1)==(b>>r&1);--r);
    a&=(1ll<<(r+1))-1,b&=(1ll<<(r+1))-1;
    res=(1ll<<(r+1))-a;
    for(k=r-1;(~k)&&(b>>k&1^1);--k);
    if(a>(1ll<<(k+1)))res-=a-(1ll<<(k+1));
    printf("%lld\n",res);
    return 0;
}

\(E\)

我应该就是那种数据结构做傻了的人……

先说一下题解的做法再说我的吧

首先当时间足够久之后,所有数的相对顺序肯定是按\(v\)排序之后的相对顺序,记\(id_i\)表示按\(v\)排序之后\(i\)处在的位置

我们假设一开始时只有\(i\)是被选中的,那么对于一个\(x_j<x_i\)\(v_j>v_i\)的点\(j\),肯定会经过\(i\)使得自己被选中,而且按\(v\)排序之后所有在\(i,j\)之间的点都会被选中

我们对于每一个\(i\),计算出最大的\(id_j\)\(x_j<x_i\),记为\(r_i\),那么所有\([id_i,id_{r_i}]\)之间的点都会被选。同理可定义\(l_i\)。容易发现\(l,r\)都是单调不降的

那么问题转化成给定\(n\)个区间且保证左右端点单调不降,选择其中若干个使得覆盖\([1,n]\),那么我们记\(f_i\)表示\([1,i]\)都已经被覆盖的方案数,前缀和优化转移即可

我的做法在后面

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=2e5+5;
struct node{
    int x,v;
    inline bool operator <(const node &b)const{return x<b.x;}
}a[N];
int id[N],l[N],r[N],f[N],n;
inline bool cmp(const int &x,const int &y){return a[x].v<a[y].v;}
int main(){
    scanf("%d",&n);
    fp(i,1,n)scanf("%d%d",&a[i].x,&a[i].v),id[i]=i;
    sort(a+1,a+1+n),sort(id+1,id+1+n,cmp);
    fp(i,1,n)l[id[i]]=r[id[i]]=i;
    fp(i,2,n)cmax(r[i],r[i-1]);
    fd(i,n-1,1)cmin(l[i],l[i+1]);
    f[0]=1;
    for(R int i=1,j=0,sum=1;i<=n;++i){
        while(j<l[i]-1)sum=dec(sum,f[j++]);
        upd(f[r[i]],sum),upd(sum,sum);
    }
    printf("%d\n",f[n]);
    return 0;
}

首先先按\(x\)排序,对于一个\(i\),如果它一开始被选就肯定会被选,否则设\(las\)\(nxt\)分别为前一个被选的和后一个被选的,设\(l\)表示\([1,las]\)中所有速度的最大值,\(r\)表示\([nxt,n]\)中所有速度的最小值,那么\(i\)不会被选当且仅当\(i\in [l,r]\)(也就是说既不可能有数从前面赶上它导致它被选,它也不可能赶上一个后面的数导致自己被选)

我们设\(las_i\)表示当\(i\)选了之后,最大的\(j\)使得\(j\)被选之后\([j+1,i-1]\)这个区间内每一个数都会被选,那么可以二分,判断的时候在主席树上判一下就行了

然后设\(f[i]\)表示\(i\)必须选的方案数,那么上一个被选的可以是\([las[i],i-1]\)中的任意一个数,前缀和优化一下就好了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
inline int max(R int x,R int y){return x>y?x:y;}
inline int min(R int x,R int y){return x<y?x:y;}
const int N=2e5+5,M=N*35,inf=0x3f3f3f3f;
int ls[M],rs[M],sz[M],rt[N],nd;
void ins(int &p,int q,int l,int r,int x){
    p=++nd,ls[p]=ls[q],rs[p]=rs[q],sz[p]=sz[q]+1;
    if(l==r)return;int mid=(l+r)>>1;
    x<=mid?ins(ls[p],ls[q],l,mid,x):ins(rs[p],rs[q],mid+1,r,x);
}
int query(int p,int q,int l,int r,int ql,int qr){
    if(!(sz[p]-sz[q])||ql<=l&&qr>=r)return sz[p]-sz[q];
    int mid=(l+r)>>1,res=0;
    if(ql<=mid&&query(ls[p],ls[q],l,mid,ql,qr))return 1;
    if(qr>mid&&query(rs[p],rs[q],mid+1,r,ql,qr))return 1;
    return 0;
}
struct node{
    int x,v;
    inline bool operator <(const node &b)const{return x<b.x;}
}a[N];
int b[N],las[N],f[N],mx[N],mn[N],sum[N];
int n;
inline int find(R int pos){
    R int l=0,r=pos-1,ans=pos-1,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(mid==pos-1||mx[mid]+1>mn[pos]-1||!query(rt[pos-1],rt[mid],1,n,mx[mid]+1,mn[pos]-1))
            ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d",&n);
    fp(i,1,n)scanf("%d%d",&a[i].x,&a[i].v),b[i]=a[i].v;
    sort(a+1,a+1+n),sort(b+1,b+1+n);
    fp(i,1,n){
        a[i].v=lower_bound(b+1,b+1+n,a[i].v)-b;
        ins(rt[i],rt[i-1],1,n,a[i].v);
    }
    mx[0]=0;fp(i,1,n)mx[i]=max(mx[i-1],a[i].v);
    mn[n+1]=inf;fd(i,n,1)mn[i]=min(mn[i+1],a[i].v);
    fp(i,1,n+1)las[i]=find(i);
    f[0]=sum[0]=1;
    fp(i,1,n+1){
        f[i]=dec(sum[i-1],(las[i]?sum[las[i]-1]:0));
        sum[i]=add(sum[i-1],f[i]);
    }
    printf("%d\n",f[n+1]);
    return 0;
}

\(F\)

神仙结论题

以下设\(f(x,y)\)表示\(x,y\)辗转相除的步数且默认\(x<y\)

首先我们考虑斐波那契数列,并定义\(F[0]=F[1]=1\),容易发现\(f(F[i],F[i+1])=i\)且对于所有\(f(a,b)=i\),有\(a\geq F[i],b\geq F[i+1]\)。所以第一个问题很简单就能搞出来

我们定义一个数对\(f(a,b)\)是好的,当且仅当不存在\(c<a,d<b\)\(f(c,d)>f(a,b)\),那么只有好的数对会对答案有贡献

我们定义一个数对\(f(a,b)\)是优秀的,当且仅当若\(f(a,b)=k\),则\(a,b\leq F[k+2]+F[k-1]\)

有一个结论:任何一个好的数对经过一次辗转相除之后会变成一个优秀的数对

考虑反证法,假设\(a=y,b=py+x\),且\(f(a,b)=k+1\),那么辗转相除之后变成\((x,y)\),如果\((x,y)\)不是优秀的数对,则\(y>F[k+2]+F[k-1]\),从而有

\[\begin{aligned}&a=y>F[k+2]\\&x>F[k]\\&b=py+x\geq y+x>F[k+2]+F[k-1]+F[k]=F[k+3]\\\end{aligned}\]

所以存在\(c=F[k+2],d=F[k+3]\),使得\(f(c,d)>f(a,b)\),这与\((a,b)\)是好的数对不符

这样我们就证明了任意一个好的数对经过一次辗转相除之后会变成一个优秀的数对,而优秀的数对并不会太多(据说满足\(f(a,b)=k\)的优秀的数对个数是\(O(k)\)?不过我并不会证明)

那么我们打表把所有\(f(a,b)=k\)的优秀的数对打出来就好了,询问的时候用优秀的数对去生成好的数对即可

//quming
#include<bits/stdc++.h>
#define R register
#define pb emplace_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
typedef long long ll;
const int N=105,M=90;
struct node{
    ll x,y;
    inline node(R ll xx,R ll yy):x(xx),y(yy){}
};vector<node>g[N];
ll f[N],n,m,T,res,cnt;
int main(){
    f[0]=f[1]=1;fp(i,2,M)f[i]=f[i-1]+f[i-2];
    g[1].pb(node(1,2)),g[1].pb(node(1,3)),g[1].pb(node(1,4));
    fp(i,1,M-3){
        for(auto v:g[i]){
            R ll x=v.y,y=v.x+x;
            while(y<=f[i+3]+f[i])g[i+1].pb(node(x,y)),y+=x;
        }
    }
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);if(n>m)swap(n,m);
        for(cnt=1;f[cnt+1]<=n&&f[cnt+2]<=m;++cnt);
        if(cnt==1){printf("%lld %lld\n",cnt,n*m%P);continue;}
        res=0;
        for(auto v:g[cnt-1]){
            R ll x=v.x,y=v.y;
            if(y<=n)res+=(m-x)/y%P;
            if(y<=m)res+=(n-x)/y%P;
            res%=P;
        }
        printf("%lld %lld\n",cnt,res);
    }
    return 0;
}
02-14 01:14