[NOIP模拟13]题解

A.矩阵游戏

其实挺水的?

考场上根本没有管出题人的疯狂暗示(诶这出题人有毛病吧这么简单的东西写一大堆柿子),而且推公式能力近乎没有,所以死掉了。

很显然乘法有交换率结合率所以操作顺序对最终结果没什么影响对吧,垃圾如我都能一眼看出来。

统计一下每行总共乘的倍数$h_i$,每列总共乘的倍数$l_i$,

之后考虑$O(n^2)$怎么求出答案:$ans=sum limits _{i=1}^{n}h_i sum limits _{j=1}^{m}l_j imes((i-1) imes m+j)$

化简一下:$sum limits_{i=1}^{n}h_i sum limits_{j=1}^{m} l_j imes (i-1) imes m+l_j imes j$

继续:$sum h_isum l_j imes j + sum h_i imes (i-1) imes m sum l_j$

显然可以$O(n)$了吧

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1000005,M=100005;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m,K;
ll hang[N],lie[N],sum,ans;
int toth,totl;

int main()
{
    n=read();m=read();K=read();
    for(int i=1;i<=n;i++)hang[i]=1;
    for(int i=1;i<=m;i++)lie[i]=1;
    char str[3];
    for(int i=1;i<=K;i++)
    {
        scanf("%s",str);
        if(str[0]=='R')
        {
            int num=read();ll val=1LL*read();
            (hang[num]*=val)%=mod;
        }
        else
        {
            int num=read();ll val=1LL*read();
            (lie[num]*=val)%=mod;
        }
    }
    ll sumhang=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        ll now=1LL*(i-1)*m+1;
        now%=mod;
        sum+=now*hang[i]%mod,sum%=mod;
        sumhang+=hang[i],sumhang%=mod;
        
    }
    for(int i=1;i<=m;i++)
    {
        ans+=sum*lie[i]%mod,ans%=mod;
        sum+=sumhang,sum%=mod;
    }
    cout<<ans<<endl;
    return 0;
}
View Code

B.跳房子

RT,跳楼题

我这样的菜b就只能想到大模拟,然而大佬就是能想出来线段树+置换群+快速幂的做法。

(我就是没脸我就要粘blog)

关于代码实现,说几点细节。

可以让$a[0][j]=a[n][j],a[n+1][j]=a[1][j]$,这样写找下一步的函数就比较简单,修改的时候特判一下就好。

置换的运算:$res[i]=b[a[i]]$,可以直接丢到快速幂里跑实现倍增的效果。

询问的时候暴力部分记得分类全,考虑它是否能越界。

query函数如果直接把映射当参传的话记得取地址。

C.优美序列

部分分还是很多而且比较好拿的,由于值域范围比较友好我们可以记录一下每个编号对应的位置$pos[]$,然后用数据结构分别维护$a[]$和$pos[]$序列上的最大最小值。之后对于每个询问$[l,r]$,先查询$[l,r]$的编号最大最小值,然后就得到了一段值域,再在这段值域上查询位置的最左和最右端点。如果最左和最右端点都在$[l,r]$内(事实上,只有可能查出来就是$[l,r]$,在它之内是不可能的),那么$[l,r]$即为所求。否则,将新得到的最左最右端点作为新的$l$和$r$再次询问。重复这个过程,最后一定能得到解。

这种办法考场上就已经码出来了,后来又分别用线段树和ST表实现了一下。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;


const int L=1<<20|1;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)


int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int a[N],n,m,pos[N];
#define ls(k) k<<1
#define rs(k) k<<1|1
int maxx[N<<2],minn[N<<2],pmax[N<<2],pmin[N<<2];
int lastl,lastr;
void build(int k,int l,int r)
{
    if(l==r)
    {
        maxx[k]=minn[k]=a[l];
        pmax[k]=pmin[k]=pos[l];
        return ;
    }
    int mid=l+r>>1;
    build(ls(k),l,mid);
    build(rs(k),mid+1,r);
    maxx[k]=max(maxx[ls(k)],maxx[rs(k)]);
    minn[k]=min(minn[ls(k)],minn[rs(k)]);
    pmax[k]=max(pmax[ls(k)],pmax[rs(k)]);
    pmin[k]=min(pmin[ls(k)],pmin[rs(k)]);
}
int qmax(int k,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)
    {
        return maxx[k];
    }
    int mid=l+r>>1,res=0;
    if(L<=mid)res=max(res,qmax(ls(k),l,mid,L,R));
    if(R>mid)res=max(res,qmax(rs(k),mid+1,r,L,R));
    return res;
}
int qmin(int k,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)
    {
        return minn[k];
    }
    int mid=l+r>>1,res=0x3f3f3f3f;
    if(L<=mid)res=min(res,qmin(ls(k),l,mid,L,R));
    if(R>mid)res=min(res,qmin(rs(k),mid+1,r,L,R));
    return res;
}
int qpmax(int k,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)
    {
        return pmax[k];
    }
    int mid=l+r>>1,res=0;
    if(L<=mid)res=max(res,qpmax(ls(k),l,mid,L,R));
    if(R>mid)res=max(res,qpmax(rs(k),mid+1,r,L,R));
    return res;
}
int qpmin(int k,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)
    {
        return pmin[k];
    }
    int mid=l+r>>1,res=0x3f3f3f3f;
    if(L<=mid)res=min(res,qpmin(ls(k),l,mid,L,R));
    if(R>mid)res=min(res,qpmin(rs(k),mid+1,r,L,R));
    return res;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),pos[a[i]]=i;
    build(1,1,n);
    m=read();
    while(m--)
    {
        int l=read(),r=read();
        if(l==r)
        {
            printf("%d %d
",l,r);
            continue;
        }
        else if(l==1&&r==n)
        {
            printf("%d %d
",1,n);
            continue;
        }
        lastl=lastr=0;
        while(l!=lastl||r!=lastr)
        {
            lastl=l;lastr=r;
            int maxval=qmax(1,1,n,l,r);
            int minval=qmin(1,1,n,l,r);
            l=qpmin(1,1,n,minval,maxval);
            r=qpmax(1,1,n,minval,maxval);
        }
        printf("%d %d
",l,r);
    }
    return 0;
}
线段树:76pts
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define re register
const int N=100005;

const int L=1<<20|1;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)

int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int a[N],n,m,pos[N],stmax[N][21][2],lg[N]={-1},stmin[N][21][2];
int lastl,lastr;
void ini()
{
    for(re int i=1;i<=n;i++)
        stmax[i][0][0]=stmin[i][0][0]=a[i],stmax[i][0][1]=stmin[i][0][1]=pos[i],lg[i]=lg[i>>1]+1;
    for(re int i=1;i<=lg[n];i++)
        for(re int j=1;j+(1<<i)-1<=n;j++)
            for(re int k=0;k<=1;k++)
            stmax[j][i][k]=max(stmax[j][i-1][k],stmax[j+(1<<(i-1))][i-1][k]),
            stmin[j][i][k]=min(stmin[j][i-1][k],stmin[j+(1<<(i-1))][i-1][k]);
}
inline int qmax(int l,int r,int k)
{
    int len=lg[r-l+1];
    return max(stmax[l][len][k],stmax[r-(1<<len)+1][len][k]);
}
inline int qmin(int l,int r,int k)
{
    int len=lg[r-l+1];
    return min(stmin[l][len][k],stmin[r-(1<<len)+1][len][k]);
}
void test()
{
    int L=read(),R=read();
    cout<<qmax(L,R,0)<<' '<<qmin(L,R,0)<<endl;
}
int main()
{
    n=read();
    for(re int i=1;i<=n;i++)
        a[i]=read(),pos[a[i]]=i;
    ini();
    //while(1)test();
    m=read();
    while(m--)
    {
        int l=read(),r=read();
        if(l==r)
        {
            printf("%d %d
",l,r);
            continue;
        }
        else if(l==1&&r==n)
        {
            printf("%d %d
",1,n);
            continue;
        }
        lastl=lastr=0;
        while(l!=lastl||r!=lastr)
        {
            lastl=l;lastr=r;
            int maxval=qmax(l,r,0);
            int minval=qmin(l,r,0);
            l=qmin(minval,maxval,1);
            r=qmax(minval,maxval,1);
        }
        printf("%d %d
",l,r);
    }
    return 0;
}
ST表:84pts

这种算法对于随机数据已经相当优秀了,奈何数据就是要卡你这种复杂度没有保证的qjyq。满分做法也很多,分块预处理询问/线段树扫描线/线段树建图+缩点+线段树查询并集 都可以做。我写了一下理论复杂度最低但常数最大会被第一种踩爆的建图做法。

(时间不够了先放代码题解回头补咕咕咕)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
#define ls(k) k<<1
#define rs(k) k<<1|1

const int L=1<<20|1;
char buffer[L],*S,*T;
#define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)

int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int N=400005;
int n,m,a[N],pos[N];
struct qj
{
    int l,r;
    qj()
    {
        l=0x3f3f3f3f;r=0;
    }
    qj(int l1,int r1)
    {
        l=l1,r=r1;
    }
};
qj operator + (const qj &x,const qj &y)
{
    return qj(min(x.l,y.l),max(x.r,y.r));
}
struct segtree
{
    qj t[N];
    void change(int k,int l,int r,int loc,qj val)
    {
        if(l==r)
        {
            t[k]=val;
            return ;
        }
        int mid=l+r>>1;
        if(loc<=mid)change(ls(k),l,mid,loc,val);
        else change(rs(k),mid+1,r,loc,val);
        t[k]=t[ls(k)]+t[rs(k)];
    }
    qj query(int k,int l,int r,int L,int R)
    {
        if(L<=l&&R>=r)return t[k];
        int mid=l+r>>1;
        if(L<=mid&&R>mid)
            return query(ls(k),l,mid,L,R)+query(rs(k),mid+1,r,L,R);
        if(L<=mid)
            return query(ls(k),l,mid,L,R);
        if(R>mid)
            return query(rs(k),mid+1,r,L,R);
    }
}seg[2];
vector<int> g1[N],g2[N];
void build(int k,int l,int r)
{
    if(l==r)
    {
        pos[l]=k;
        return ;
    }
    int mid=l+r>>1;
    build(ls(k),l,mid);
    build(rs(k),mid+1,r);
    g1[k].push_back(ls(k));
    g1[k].push_back(rs(k));
}
void addedge(int k,int l,int r,int L,int R,int node)
{
    if(L<=l&&R>=r)
    {
        g1[node].push_back(k);
        return ;
    }
    int mid=l+r>>1;
    if(L<=mid)addedge(ls(k),l,mid,L,R,node);
    if(R>mid)addedge(rs(k),mid+1,r,L,R,node);
}
qj t1[N],t2[N];
int low[N],dfn[N],ind,st[N],bel[N],scc,vis[N],top;
void tarjan(int x)
{
    dfn[x]=low[x]=++ind;
    vis[x]=1;st[++top]=x;
    int sz=g1[x].size();
    for(int i=0;i<sz;i++)
    {
        int y=g1[x][i];
        if(!dfn[y])
            tarjan(y),low[x]=min(low[x],low[y]);
        else if(vis[y])
            low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        scc++;
        int now;
        do
        {
            now=st[top--];
            vis[now]=0;
            bel[now]=scc;
        }while(now!=x);
    }
}
bool v[N];
void dfs(int x)
{
    if(v[x])return ;
    v[x]=1;
    int sz=g2[x].size(),y;
    for(int i=0;i<sz;i++)
        y=g2[x][i],dfs(y),t2[x]=t2[x]+t2[y];
}
int main()
{
    n=read();build(1,1,n);
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=n;i++)
        seg[0].change(1,1,n,a[i],{i,i});
    for(int i=2;i<=n;i++)
    {
        int x=min(a[i-1],a[i]),y=max(a[i-1],a[i]);
        t1[pos[i]]=seg[0].query(1,1,n,x,y);
        addedge(1,1,n,t1[pos[i]].l+1,t1[pos[i]].r,pos[i]);
    }
    for(int i=1;i<N-4;i++)
        if(!dfn[i])tarjan(i);
    for(int x=1;x<N-4;x++)
    {
        int sz=g1[x].size();//if(!sz)continue;
        for(int i=0;i<sz;i++)
        {
            int y=g1[x][i];
            if(bel[x]!=bel[y])
                g2[bel[x]].push_back(bel[y]);
        }
    }
    for(int i=1;i<N-4;i++)
        t2[bel[i]]=t2[bel[i]]+t1[i];
    for(int i=1;i<=scc;i++)
        dfs(i);
    for(int i=2;i<=n;i++)
        seg[1].change(1,1,n,i,t2[bel[pos[i]]]);
    m=read();
    while(m--)
    {
        int l=read(),r=read();
        if(l==r)
        {
            printf("%d %d
",l,r);
            continue;
        }
        qj ans=seg[1].query(1,1,n,l+1,r);
        printf("%d %d
",ans.l,ans.r);
    }
    return 0;
}
又臭又长
原文地址:https://www.cnblogs.com/Rorschach-XR/p/11312066.html