7.17训练总结

考场错误:
注意二分的边界问题,写 l = m i d + 1 , r = m i d − 1 l=mid+1,r=mid-1 l=mid+1,r=mid1的话是 w h i l e ( l ≤ r ) while(l\le r) while(lr)
注意一些corner case,多想一想

补题

gym103687F Easy Fix

首先,使用树状数组求出初始每个位置的a,b,c的值

接着考虑一次交换操作可能带来的影响,一定是来自 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r1]内部的

首先我们考虑 p [ l ] < p [ r ] p[l] < p[r] p[l]<p[r]的情况

对于内部的一个位置 j j j,如果原本 p [ l ] < p [ j ] < p [ r ] p[l]<p[j]<p[r] p[l]<p[j]<p[r],那么修改后会使得 a [ j ] − 1 , b [ j ] + 1 a[j]-1,b[j]+1 a[j]1,b[j]+1,因此当原来 a [ j ] ≤ b [ j ] a[j]\le b[j] a[j]b[j]的时候,对答案的贡献会减少1

对于内部的一个位置 j j j,如果原本 p [ l ] < p [ j ] < p [ r ] p[l]<p[j]<p[r] p[l]<p[j]<p[r],那么修改后会使得 a [ j ] − 1 , b [ j ] + 1 a[j]-1,b[j]+1 a[j]1,b[j]+1,因此当原来 b [ j ] + 1 ≤ a [ j ] − 1 b[j]+1\le a[j]-1 b[j]+1a[j]1的时候,对答案的贡献会增加1

同理当 p [ l ] > p [ r ] p[l] > p[r] p[l]>p[r]

对于内部的一个位置 j j j,如果原本 p [ r ] < p [ j ] < p [ l ] p[r]<p[j]<p[l] p[r]<p[j]<p[l],那么修改后会使得 a [ j ] + 1 , b [ j ] − 1 a[j]+1,b[j]-1 a[j]+1,b[j]1,因此当原来 b [ j ] ≤ a [ j ] b[j]\le a[j] b[j]a[j]的时候,对答案的贡献会减少1

对于内部的一个位置 j j j,如果原本 p [ l ] < p [ j ] < p [ r ] p[l]<p[j]<p[r] p[l]<p[j]<p[r],那么修改后会使得 a [ j ] + 1 , b [ j ] − 1 a[j]+1,b[j]-1 a[j]+1,b[j]1,因此当原来 a [ j ] + 1 ≤ b [ j ] − 1 a[j]+1\le b[j]-1 a[j]+1b[j]1的时候,对答案的贡献会增加1

所以开五个主席树即可解决了,每次相当于查询区间内的满足 p [ r ] < p [ j ] < p [ l ] p[r]<p[j]<p[l] p[r]<p[j]<p[l]的个数,第五个主席树用于记录l和r位置的贡献

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m;
int a[maxn],b[maxn],c[maxn],sum[maxn];
int tr[maxn],p[maxn];
int lowbit(int x)
{
    return x&-x;
}
void add(int x,int v)
{
    while(x<=n)
    {
        tr[x]+=v;
        x+=lowbit(x);
    }
}
int query(int x)
{
    int res=0;
    while(x)
    {
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}
struct chairman
{
    int rt[maxn],tot;
    int ls[maxn<<5],rs[maxn<<5],val[maxn<<5];
    void cpy(int u,int v)
    {
        ls[u]=ls[v]; rs[u]=rs[v]; val[u]=val[v];
    }
    void modify(int &u,int v,int l,int r,int pos,int gs)
    {
        u=++tot; cpy(u,v);
        val[u]+=gs;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) modify(ls[u],ls[v],l,mid,pos,gs);
        else modify(rs[u],rs[v],mid+1,r,pos,gs);
    }
    int query(int s,int t,int l,int r,int L,int R)
    {
        // cerr<<s<<" "<<t<<" "<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
        if(l>=L && r<=R)
            return val[t]-val[s];
        int mid=l+r>>1;
        if(R<=mid) return query(ls[s],ls[t],l,mid,L,R);
        if(mid<L) return query(rs[s],rs[t],mid+1,r,L,R);
        return query(ls[s],ls[t],l,mid,L,R)+query(rs[s],rs[t],mid+1,r,L,R);
    }
}A,B,C,D,E;
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)
    {
        a[i]=query(p[i]);
        b[i]=p[i]-a[i]-1;
        c[i]=min(a[i],b[i]);
        sum[i]=sum[i-1]+c[i];
        add(p[i],1);
    }
    // cerr<<sum[n];
    // return 0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]<=b[i]) A.modify(A.rt[i],A.rt[i-1],1,n,p[i],-1);
        else A.rt[i]=A.rt[i-1];
        if(a[i]-1>=b[i]+1) B.modify(B.rt[i],B.rt[i-1],1,n,p[i],1);
        else B.rt[i]=B.rt[i-1];
        if(a[i]>=b[i]) C.modify(C.rt[i],C.rt[i-1],1,n,p[i],-1);
        else C.rt[i]=C.rt[i-1];
        if(b[i]-1>=a[i]+1) D.modify(D.rt[i],D.rt[i-1],1,n,p[i],1);
        else D.rt[i]=D.rt[i-1];
        E.modify(E.rt[i],E.rt[i-1],1,n,p[i],1);
    }
    // return 0;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        int l,r;
        // cerr<<i;
        scanf("%d%d",&l,&r);
        if(l==r)
        {
            printf("%d\n",sum[n]);
            continue;
        }
        if(l>r) swap(l,r);
        int ans=sum[n]-c[l]-c[r],tmp;
        if(p[l]<p[r])
        {
            ans+=A.query(A.rt[l],A.rt[r-1],1,n,p[l],p[r]);
            // return 0;
            ans+=B.query(B.rt[l],B.rt[r-1],1,n,p[l],p[r]);
            // return 0;
            tmp=E.query(E.rt[0],E.rt[l-1],1,n,1,p[r]);
            ans+=min(tmp,p[r]-1-tmp);
            tmp=E.query(E.rt[r],E.rt[n],1,n,1,p[l]);
            ans+=min(tmp,p[l]-1-tmp);
            // cerr<<ans;
            // return 0;
        }
        else
        {
            ans+=C.query(C.rt[l],C.rt[r-1],1,n,p[r],p[l]);
            ans+=D.query(D.rt[l],D.rt[r-1],1,n,p[r],p[l]);
            tmp=E.query(E.rt[0],E.rt[l-1],1,n,1,p[r]);
            ans+=min(tmp,p[r]-1-tmp);
            tmp=E.query(E.rt[r],E.rt[n],1,n,1,p[l]);
            ans+=min(tmp,p[l]-1-tmp);
        }
        printf("%d\n",ans);
    }
    return 0;
}

Gym - 103687I

首先,如果这个子串是回文的,先手就输了,这个可以通过哈希来判断

接着,考虑所有先手必输的情况,即取两段后的结果都是回文串,可以通过观察发现,这类串的长度为偶数

综合以上两个情况,可以完成代码,复习了字符串哈希,可以尝试使用马拉车实现回文串的判断!

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn=1e6+5;
const ull base=233333;
int n,q;
ull has[maxn],Has[maxn],pw[maxn];
char s[maxn];
bool is_pr(int l,int r)
{
    if(has[r]-has[l-1]*pw[r-l+1]==Has[l]-Has[r+1]*pw[r-l+1])
        return true;
    return false;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    pw[0]=1;
    for(int i=1;i<=n;i++)
    {
        pw[i]=pw[i-1]*base;
        has[i]=has[i-1]*base+s[i];
    }
    for(int i=n;i>=1;i--) Has[i]=Has[i+1]*base+s[i];
    while(q--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        if(is_pr(l,r) || (r-l)&1) printf("Budada\n");
        else printf("Putata\n");
    }
    return 0;
}

Gym - 103687J

计算几何好久不写,十分生疏,这算是一个复习的开始吧,注意顺时针和逆时针的方向向量的不同,一些细节一定要注意!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double PI=acos(-1);
int T;
int st,ed;
struct node
{
    double x,y;
    node operator + (node &oth) const
    {
        return (node){x+oth.x,y+oth.y};
    }
    node operator - (node &oth) const
    {
        return (node){x-oth.x,y-oth.y};
    }
}s,t;
int cnt;
node get(int ang)
{
    node res;
    res.x=cos(PI*ang/180);
    res.y=sin(PI*ang/180);
    return res;
}
double sqr(double x)
{
    return x*x;
}
double dist(node x,node y)
{
    return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
}
void print(node x)
{
    printf("%.10lf %.10lf\n",x.x,x.y);
}
node rot_90(node x)
{
    return (node){-x.y,x.x};
}
double cross(node a,node b)
{
    return a.x*b.y-a.y*b.x;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&T);
    while(T--)
    {
        int rev=0;
        scanf("%d%d",&st,&ed);
        s=get(st); t=get(ed);
        int dis=min((st-ed+360)%360,(ed-st+360)%360);
        if(!dis)
        {
            printf("0\n");
            print(s);
            continue;
        }
        if(dis<=90)
        {
            node dir=s-t; dir=rot_90(dir);
            double d=dist(s,t);
            // cerr<<t.x<<" "<<sin(PI/2);
            dir.x/=d; dir.y/=d;
            // printf("!");
            if(cross(s,t)<0) dir.x*=-1,dir.y*=-1;
            // print(dir);
            double l=sqrt(1-d*d/4);
            // cerr<<l;
            node M; M.x=(s.x+t.x)/2; M.y=(s.y+t.y)/2;
            dir.x*=l,dir.y*=l;
            node tmp=M+dir;
            printf("%d\n",2);
            // cerr<<t.x<<" "<<t.y;
            print(s); print(tmp); print(t);
            continue;
        }
        if(dis<=131)
        {
            node dir=s-t,Dir=s-t; dir=rot_90(dir);
            double d=dist(s,t);
            dir.x/=d; dir.y/=d; Dir.x/=d*2; Dir.y/=d*2;
            if(cross(s,t)<0) dir.x*=-1,dir.y*=-1;
            node tmp1,tmp2;
            double l=sqrt(1-sqr(d/2-0.5));
            dir.x*=l,dir.y*=l;
            node M; M.x=(s.x+t.x)/2; M.y=(s.y+t.y)/2;
            tmp1=M+dir+Dir; tmp2=M+dir-Dir;
            printf("3\n");
            print(s); print(tmp1); print(tmp2); print(t);
            continue;
        }
        node Dir=rot_90(s);
        if(cross(s,t)<0) Dir.x*=-1,Dir.y*=-1;
        node tmp1=s+Dir,tmp2=tmp1-s;
        printf("4\n");
        print(s); print(tmp1); print(tmp2);
        s=tmp2;
        node dir=s-t; dir=rot_90(dir);
        double d=dist(s,t);
        dir.x/=d; dir.y/=d;
        if(cross(s,t)<0) dir.x*=-1,dir.y*=-1;
        double l=sqrt(1-d*d/4);
        node M; M.x=(s.x+t.x)/2; M.y=(s.y+t.y)/2;
        dir.x*=l,dir.y*=l;
        node tmp=M+dir;
        print(tmp); print(t);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值