AGC 015

A
求一下最后和的范围l~r,之间的数都可取到
B
每层楼,如果是往上走的,他到下面的楼层都一定至少2次,到上面的1次,往下走的同理

C
每个联通块两两之间只有一条路径,所以每个联通块是树的结构,n块由n-1条邻边连接
那么每条两边都是黑色块的邻边,能且一定能合并两个联通块,减少1个联通块,给定矩形内的黑色联通块数量就是黑色块的数量减去连边都是黑色块的邻边数量

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 2100;

int n,m,q;
char str[maxn];
int a[maxn][maxn];
int s[maxn][maxn];
int re[maxn][maxn],ce[maxn][maxn];

int main()
{
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++) 
    {
        scanf("%s",str+1);
        for(int j=1;j<=m;j++) 
            a[i][j]=str[j]-'0',
            s[i][j]=s[i][j-1]+a[i][j],
            re[i][j]=re[i][j-1]+(a[i][j]&a[i][j-1]);
        for(int j=1;j<=m;j++) s[i][j]+=s[i-1][j],re[i][j]+=re[i-1][j];
    }
    for(int j=1;j<=m;j++)
    {
        for(int i=1;i<=n;i++) ce[i][j]=ce[i-1][j]+(a[i][j]&a[i-1][j]);
        for(int i=1;i<=n;i++) ce[i][j]+=ce[i][j-1];
    }
    while(q--)
    {
        int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int ans=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];
        int n1=0;
        if(y1<y2) n1=re[x2][y2]-re[x2][y1]-re[x1-1][y2]+re[x1-1][y1];
        int n2=0;
        if(x1<x2) n2=ce[x2][y2]-ce[x2][y1-1]-ce[x1][y2]+ce[x1][y1-1];
        printf("%d\n",ans-n1-n2);
    }   

    return 0;
}

D
若A=B,答案显然是1
否则找到A,B不同的最高二进制位k,高于k位的二进制位因为A~B的数这一位都相同,可以都视为0,那么有 0<=A<2k,2k<=B<2k+1
将A~B分为 [A,2k),[2k,B] 两个集合,A集合内选一个子集或,能且仅能或出 [A,2k) ,在B集合内选一个子集或,设B在k位以下最高非0位是第u位,则B集合内选子集或能得到 [2k,2k+2u+1) ,若既选A又选B集合内的数,能或出 [2k+A,2k+1) 三个集合取并就是答案

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 2100;

ll A,B;
ll ans;

int main()
{
    scanf("%lld%lld",&A,&B);
    if(A==B) return puts("1"),0;

    int now=60; ll al=(1ll<<61)-1;
    for(;now>=0;now--) 
    {
        if((A>>now&1)^(B>>now&1)) break;
        A&=al^(1ll<<now),B&=al^(1ll<<now);
    }
    int top=now-1;
    for(;top>=0&&!(B&(1ll<<top));top--); if(top<0) top=now;
    ll l=top==now?B:((1ll<<now)+(1ll<<top+1)-1);
    ll r=(1ll<<now)+A;
    ans=(1ll<<now+1)-A;
    if(l+1<r) ans-=r-1ll-l;

    printf("%lld\n",ans);

    return 0;
}

E
若将点u(x,v)染红,它能够染红的点有4种:
1)x’< x,v’>v;
2)x’>x,v’< v;
3)x’< x,v’< v且v’>某个2类点的v;
4)x’>x,v’>v且v’< 某个1类点的v
按v排序后,对于u,最左边的2类点为l,最右边的1类点为r,那么点u能染红区间[l,r],然后能发现,这些区间之间不存在完全包含的关系,即按照L为第一关键字,R为第二关键字排序后,有L1<=L2<=L3<=..<=LN,R1<=R2<=R3<=..RN
按照R为第一关键字,L为第二关键字排序,f[n]表示染了1~n的方案数,维护一个前缀和,就可以dp了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;
const ll Mod = 1e9+7;

int n;
struct Taka{int p,v;}p[maxn];
inline bool cmpv(const Taka x,const Taka y){return x.v<y.v;}
struct node
{
    int l,r;
    node(){}
    node(const int _l,const int _r){l=_l,r=_r;}
}a[maxn];
inline bool cmpr(const node x,const node y){return x.r==y.r?x.l<y.l:x.r<y.r;}

struct segment{int mx,mn;}seg[maxn<<2];
void build(const int x,const int l,const int r)
{
    if(l==r) {seg[x].mx=seg[x].mn=p[l].p; return;}
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    build(lc,l,mid); build(rc,mid+1,r);
    seg[x].mx=max(seg[lc].mx,seg[rc].mx);
    seg[x].mn=min(seg[lc].mn,seg[rc].mn);
}
int lx,rx,c;
int qleft(const int x,const int l,const int r)
{
    if(rx<l||seg[x].mx<c) return -1;
    if(l==r) return l;
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    int re=qleft(lc,l,mid);
    return re==-1?qleft(rc,mid+1,r):re;
}
int qright(const int x,const int l,const int r)
{
    if(r<lx||seg[x].mn>c) return -1;
    if(l==r) return l;
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    int re=qright(rc,mid+1,r);
    return re==-1?qright(lc,l,mid):re;
}

ll f[maxn],sum[maxn],al;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&p[i].p,&p[i].v);
    sort(p+1,p+n+1,cmpv);
    build(1,1,n);

    for(int i=1;i<=n;i++)
    {
        lx=rx=i; c=p[i].p;
        a[i]=node(qleft(1,1,n),qright(1,1,n));
    }sort(a+1,a+n+1,cmpr);

    al=sum[0]=f[0]=1ll; int pos=1;
    for(int i=1;i<=n;i++)
    {
        while(pos<=n&&a[pos].r==i)
        {
            ll tmp=al-(a[pos].l-2>=0?sum[a[pos].l-2]:0);
            (f[i]+=tmp)%=Mod;
            (al+=tmp)%=Mod;
            pos++;
        }
        sum[i]=(sum[i-1]+f[i])%Mod;
    }
    printf("%lld\n",(f[n]+Mod)%Mod);

    return 0;
}

F
倒着推gcd的过程,(y,x)->(x,px+y)。
我们定义f(x,y)为(x,y)的欧几里得步数,若对于(x,y),不存在x’<=x,y’<=y且(x’,y’)>(x,y),则称(x,y)为good pair
易知答案就是所有x<=n,y<=m的good pairs。
为了方便说明,不妨对于每对(x,y),设x< y。
我们构造出x,y最小的pair,即每次从k阶->k+1阶时,(x,y)->(y,x+y),发现就是斐波那契数列,即x,y最小的k阶pair就是(Fk,Fk+1),用归纳可证对于任意的k阶pair(x,y)有x>=Fk,y>=Fk+1。
我们想要生成所有k阶的good pairs,然鹅good pairs可以像(2,2017)这样很畸形,但我们对他做一次欧几里得算法他会好看很多。
定义k阶的excellent pair为f(x,y)=k,x,y<=Fk+2。
用反证法可以证明一个good pair做一次欧几里得算法后一定会变成excellent pair,而excellent pair一定是good pair,对他做欧几里得算法他也会变成k-1阶的excellent pair即他可以由k-1的excellent pair倒推得到
同时易证k阶的excellent pair只有k+1个,所以可以log^2预处理出所有阶的excellent pair
求解时先求出x<=n,y<=m的good pair是k阶,再用k-1阶excellent pair的推出k阶的good pairs在限制内的有多少对

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e18
using namespace std;

const int maxn = 310;
const ll Mod = 1e9+7;

struct node
{
    ll x,y;
    node(){}
    node(const ll _x,const ll _y){x=_x;y=_y;}
}a[maxn][maxn]; int kn[maxn];
ll f[maxn];

void G(int k) // gernerate k+1
{
    for(int i=1;i<=kn[k];i++)
    {
        ll y=a[k][i].x,x=a[k][i].y;
        for(int j=1;;j++)
        {
            y+=x; if(y>f[k+3]) break;
            a[k+1][++kn[k+1]]=node(x,y);
        }
    }
}
ll solve(ll n,ll m,int k)
{
    ll re=0;
    if(k<=0)
    {
        for(int i=1;i<=n;i++)
        {
            ll k1=n/i,k2=m/i;
            (re+=k1*2ll%Mod-1+k2-k1)%=Mod;
        }
    }
    else
    {
        for(int i=1;i<=kn[k];i++)
        {
            ll y=a[k][i].x,x=a[k][i].y;
            // x px+y
            if(x>n) continue;
            ll k1=(n-y)/x,k2=(m-y)/x;
            (re+=k1*2ll%Mod+k2-k1)%=Mod;
        }
    }
    return re;
}

int main()
{
    f[0]=f[1]=1ll; for(int i=2;f[i-2]<=inf;i++) f[i]=f[i-1]+f[i-2];
    a[1][1]=node(1,2); a[1][2]=node(1,3); kn[1]=2;
    for(int i=2;f[i]<=inf;i++) 
        G(i-1);

    int t; scanf("%d",&t);
    while(t--)
    {
        ll x,y; scanf("%lld%lld",&x,&y); if(x>y) swap(x,y);
        int k;  for(k=1;f[k+1]<=y&&f[k]<=x;k++);k--;
        k--; if(k<0) k=0;
        ll ans=solve(x,y,k);
        printf("%d %lld\n",k+1,(ans+Mod)%Mod);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值