10月19日考试 题解(模拟+贪心+树状数组+线段树+博弈论)

T1 贪吃蛇

题目大意:给定地图大小,障碍坐标和蛇一开始坐标。蛇一开始长度为$1$。$q$次询问,每次有两种操作:1.向上、下、左、右伸长一格;2.尾巴缩短一格。当蛇越界或撞到障碍物或自己身体时游戏结束。问游戏结束的时间。

STL::deque模拟即可。

代码:

#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
const int N=105;
int a[N][N],vis[N][N],n,m,t,Q;
int nowx,nowy;
deque< pair<int,int> > q;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline bool judge(int x,int y)
{
    if (x<1||x>n||y<1||y>m) return 0;
    if (vis[x][y]||a[x][y]) return 0;
    return 1;
}
int main()
{
//    freopen("snake18.in","r",stdin);
    n=read();m=read();t=read();Q=read();
    for (int i=1;i<=t;i++)
    {
        int x=read(),y=read();
        a[x][y]=1;
    }
    nowx=read();nowy=read();
    vis[nowx][nowy]=1;
    q.push_back(make_pair(nowx,nowy));
    for (int i=1;i<=Q;i++)
    {
        int opt=read();
        if (opt==1)
        {
            char c;cin>>c;
            int xx,yy;
            if (c=='U') xx=nowx-1,yy=nowy;
            if (c=='D') xx=nowx+1,yy=nowy;
            if (c=='L') xx=nowx,yy=nowy-1;
            if (c=='R') xx=nowx,yy=nowy+1;
            if (!judge(xx,yy)){
                cout<<i;
                return 0;
            }
            q.push_back(make_pair(xx,yy));
            vis[xx][yy]=1;
            nowx=xx,nowy=yy;
        //    printf("%d %d\n",xx,yy);
        }
        else
        {
            pair<int,int> tmp=q.front();q.pop_front();
            vis[tmp.first][tmp.second]=0;
        }
    }
    cout<<-1;
    return 0;
}

T2 分糖果

原题目:皇后游戏

先说$cmp$:$\min(a_i,b_j)<\min(b_i,a_j)$

我的思路跟高赞题解的不同,欢迎探讨。(参考自2014年北京高考理科数学第20题)

我们可以尝试用数学归纳法证明贪心策略。

当$n=2$时,假设现有两个数对数列$P(a,b)(c,d)$和$P'(c,d)(a,b)$。

$T_1(P)=a+b,T_2(P)=\max(a+b,a+c)+d=a+d+\max(b,c)$

$T_1(P')=c+d,T_2(P')=max(c+a,c+d)+b$

当$min=a$时,$T_2(P')=c+d+b$。因为有$a\leq max(b,c)$,所以$T_2(P)\leq T_2(p')$;
当$min=d$时,$T_2(P')=c+a+b$。因为有$d\leq max(b,c)$,所以此时有$T_2(P)\leq T_2(p')$。

综上,$n=2$时贪心策略成立。$n>2$时,因为满足局部最优子结构的性质,由数学归纳法得到贪心策略是正确的。

以上说明了$\min(a_i,b_j)$严格小于$\min(b_i,a_j)$的情况。对于相等的情况,我们怎么处理?事实上,我们有$a_i<a_j$。考虑讨论$min$的取值:

1.如果$min$取值在$a$,那么显然成立,符合贪心策略意图。

2.如果$min$取值在$b$,那么考虑这个式子:$max(\sum\limits a+a_i,c)$。现然我们要让这个式子尽量取值小,把较小的$a$放到前面是比把较大的$a$放到前面是更优的。

综上,$a_i<a_j$成立。

Upd:然而这个证明存在问题QAQ,各位看官就随便看看吧……

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N=50005;
int T,n,ans,c[N],sum;
struct node{
    int a,b;
}a[N];
bool cmp(node a,node b){
    return min(a.a,b.b)==min(b.a,a.b)?a.a<b.a:min(a.a,b.b)<min(b.a,a.b);
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
signed main()
{
    T=read();
    while(T--)
    {
        n=read();
        for (int i=1;i<=n;i++)
            a[i].a=read(),a[i].b=read();
        sort(a+1,a+n+1,cmp);
        c[1]=a[1].a+a[1].b;
        ans=c[1];sum=a[1].a;
        for (int i=2;i<=n;i++)
        {
            sum+=a[i].a;
            c[i]=max(c[i-1],sum)+a[i].b;
            ans=max(ans,c[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

T3 排序

题目大意:给定长度为$n$的序列$a$和$m$个修改位置$p$。每次将$p$之后且小于等于$a_p$的数拿出来升序排序再按次序放回原位置。问每次操作过后逆序对数。

考虑每个数的贡献。通常用树状数组维护逆序对数是考虑每个$i$的贡献:$j<i$且$a_j>a_i$。我们变换一下思路,求$j>i$且$a_j<a_i$的数对个数。一个显然的二维偏序问题。这样我们可以通过预处理逆序对然后逐一减去每个数的贡献。

对于快速求出满足题目条件的数,我们可以使用线段树,维护一个$pair$类型的线段树,每次返回最小值的下标。均摊复杂度是$O(1)$的。总时间复杂度$O((n+m)\log n)$。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define lowbit(x) x&(-x)
#define int long long
using namespace std;
const int N=500005;
const int inf=1e18;
int tree[N],a[N],b[N],ans[N],num[N],n,m,now;
pair<int,int> mi[N*4];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void add(int x){
    for (int i=x;i<=n;i+=lowbit(i))
        tree[i]++;
}
inline int sum(int x){
    int res=0;
    for (int i=x;i;i-=lowbit(i))
        res+=tree[i];
    return res;
}
inline void build(int index,int l,int r)
{
    mi[index]=make_pair(inf,inf);
    if (l==r) return;
    int mid=(l+r)>>1;
    build(index*2,l,mid);
    build(index*2+1,mid+1,r);
}
inline void update(int index,int l,int r,int x,pair<int,int> k)
{
    if (l==r){
        mi[index]=k;
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) update(index*2,l,mid,x,k);
    else update(index*2+1,mid+1,r,x,k);
    mi[index]=min(mi[index*2],mi[index*2+1]);
}
inline pair<int,int> query(int index,int l,int r,int ql,int qr)
{
    if (ql<=l&&r<=qr) return mi[index];
    int mid=(l+r)>>1;pair<int,int> res=make_pair(inf,inf);
    if (ql<=mid) res=min(res,query(index*2,l,mid,ql,qr));
    if (qr>mid) res=min(res,query(index*2+1,mid+1,r,ql,qr));
    return res;
}
signed main()
{
    n=read();m=read();
    for (int i=1;i<=n;i++)
        a[i]=read(),b[i]=a[i];
    sort(b+1,b+n+1);
    int len=unique(b+1,b+n+1)-b-1;
    for (int i=n;i>=1;i--)
    {
        num[i]=lower_bound(b+1,b+len+1,a[i])-b;
        add(num[i]);ans[i]=sum(num[i]-1);
        now+=ans[i];
        update(1,1,n,i,make_pair(num[i],i));
    }
    while(m--)
    {
        int p=read();
        while(1)
        {
            pair<int,int> tmp=query(1,1,n,p,n);
            if (tmp.first>num[p]) break;
            now-=ans[tmp.second];
            update(1,1,n,tmp.second,make_pair(inf,inf));
        }
        printf("%lld\n",now);
    }
    return 0;
}

T4 眼泪

改了改,然而懒得写题解了。咕咕咕……

posted @ 2020-10-20 14:02  我亦如此向往  阅读(138)  评论(0编辑  收藏  举报