2019.08.09【NOIP提高组】模拟 A 组 比赛过程及题解

退役选手为了维持一下手感,从必刷题和金考卷的海洋的跳出来刷点题

感谢symbol一套题把我的水准由普及给拉上来了

比赛过程

早上八点挣扎着从床上起来看题

感觉T1比较像我之前搬过的一道原题,应该是\(bfs\)加上一些优化,感觉码量可能会比较大就先丢了

T2的限制条件和一年前写过的某场CF的D题是一样的,就是求的不大一样,刚起来不大想推式子就又丢了

T3应该是个比较奇妙的性质题,一眼会\(O(n^2)\),再要优化的话应该是要优化询问区间和\(check\)方式,不太想想就丢了

爆零预定

最终决定顺序开题,T1感觉就是一个最短路,8:25开始码,磕磕碰碰码了半个小时写完了,随意debug了一下就放在一边了

出去吃了个早饭(顺便%了一下准高三大佬hokiplus),路上大致发现T3只需要\(checkO(n)\)个区间就可以了,但是还是只会\(O(n^2)\)\(check\)

回来之后开\(T2\),期间有个地方想错了卡了一会,其它都还好,码完+调完10:30了

又来看T3,先花10min写了个\(60pts\),后来感觉会满分了,被一个奥妙重重的边界卡了一下,\(11:20\)写完了

jzoj这回卡了48面评测

吃完饭回来一看T3\(fst\)了,发现把\(sum[r]\)写成了\(sum[r+1]\),改完就A了,十分难受

还是要提高代码能力啊

Solution

T1

直接bfs不知道多少分啊(我可能还不会直接bfs)

考虑最短路,首先四联通的点连边,接下来考虑传送门的影响:可以使某一个点到达上下左右最近的墙,花费是到达距离这个点最近的墙的时间

建完图直接最短路就好了

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii; 
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 1061109567
#define eps 1e-8
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
struct node{int to,nxt,cost;}sq[2002000];
int all=0,head[250010];
struct hnode{int u,dis;};
bool operator <(hnode p,hnode q){return p.dis>q.dis;}
priority_queue<hnode> hp;
int n,m,L[510][510],R[510][510],U[510][510],D[510][510],mind[510][510];
int id[510][510],dis[250010],st,ed,tot=0;
bool vis[250010];
char s[510][510];
 
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;
}

void bfs()
{
    queue<pii> q;
    rep(i,1,n) rep(j,1,m)
        if (s[i][j]=='#') q.push(mp(i,j));
    while (!q.empty())
    {
        int x=q.front().fir,y=q.front().sec;q.pop();
        rep(i,0,3)
        {
            int nx=x+dx[i],ny=y+dy[i];
            if ((nx>1) && (nx<=n) && (ny>1) && (ny<=m) && (s[nx][ny]=='.') && (!mind[nx][ny]))
            {
                mind[nx][ny]=mind[x][y]+1;q.push(mp(nx,ny));
            }
        }
    }
    rep(i,1,n) rep(j,1,m)
        if (s[i][j]=='.')
        {
            if (s[i][j-1]=='#') L[i][j]=id[i][j];
            else L[i][j]=L[i][j-1];
            if (s[i-1][j]=='#') U[i][j]=id[i][j];
            else U[i][j]=U[i-1][j];
        }
    per(i,n,1) per(j,m,1)
        if (s[i][j]=='.')
        {
            if (s[i][j+1]=='#') R[i][j]=id[i][j];
            else R[i][j]=R[i][j+1];
            if (s[i+1][j]=='#') D[i][j]=id[i][j];
            else D[i][j]=D[i+1][j];
        }
}

void add(int u,int v,int w)
{
    all++;sq[all].to=v;sq[all].nxt=head[u];sq[all].cost=w;head[u]=all;
}

void build()
{
    rep(i,1,n) rep(j,1,m)
    {
        if (s[i][j]=='.')
        {
            rep(k,0,3)
            {
                int nx=i+dx[k],ny=j+dy[k];
                if (s[nx][ny]=='.') add(id[i][j],id[nx][ny],1);
            }
        }
        if (L[i][j]!=id[i][j]) add(id[i][j],L[i][j],mind[i][j]);
        if (R[i][j]!=id[i][j]) add(id[i][j],R[i][j],mind[i][j]);
        if (U[i][j]!=id[i][j]) add(id[i][j],U[i][j],mind[i][j]);
        if (D[i][j]!=id[i][j]) add(id[i][j],D[i][j],mind[i][j]);
    }
}

void dij()
{
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    hp.push((hnode){st,0});dis[st]=0;
    while (!hp.empty())
    {
        int u=hp.top().u;hp.pop();
        if (vis[u]) continue;vis[u]=1;
        for (int i=head[u];i;i=sq[i].nxt)
        {
            int v=sq[i].to;
            if (dis[v]>dis[u]+sq[i].cost)
            {
                dis[v]=dis[u]+sq[i].cost;
                if (!vis[v]) hp.push((hnode){v,dis[v]});
            }
        }
    }
}
        
int main()
{
    freopen("cell.in","r",stdin);
    freopen("cell.out","w",stdout);
    n=read();m=read();
    rep(i,1,n) scanf("%s",s[i]+1);
    rep(i,1,n) rep(j,1,m) id[i][j]=(++tot); 
    rep(i,1,n) rep(j,1,m)
        if (s[i][j]=='C') {st=id[i][j];s[i][j]='.';}
        else if (s[i][j]=='F') {ed=id[i][j];s[i][j]='.';}
    bfs();build();dij();
    if (dis[ed]<maxd) printf("%d",dis[ed]);
    else puts("no");
    return 0;
}

所以T1的码量可能是T2+T3是什么鬼

T2

如果你写过这个题的话T2就比较简单了

根据BST的性质肯定是要按照\(key\)值排个序的,预处理一下任意两个点之间是否能连边

接下来考虑\(dp\),暴力dp的话就应该是记\(dp_{i,j,k}\)表示把区间\([i,j]\)建成以\(k\)为根的\(BST\)时的答案,这样\(dp\)好像是\(O(n^5)\)的不大行,

注意到一棵\(BST\)的左右子树是互相独立的,于是可以记\(f_{i,j,0}\)表示区间\([i,j]\)将会作为\(i-1\)的右子树的答案,\(f_{i,j,1}\)表示\([i,j]\)作为\(j+1\)的左子树的答案,转移的话枚举\(i-1\)或者\(j+1\)的直系儿子是哪一个就可以了,时间是\(O(n^3)\)

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
#define int long long
typedef long long ll;
typedef long double db;
typedef pair<int,int> pii;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define ky first
#define val second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
int n,dp[310][310][2],sum[310];
pii a[310];
int g[310][310];

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 gcd(int x,int y)
{
    if (!y) return x;
    else return gcd(y,x%y);
}

signed main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read();
    rep(i,1,n) 
    {
        a[i].ky=read();a[i].val=read();
    }
    sort(a+1,a+1+n);
    rep(i,1,n) rep(j,1,n) g[i][j]=gcd(a[i].ky,a[j].ky);
    rep(i,1,n) sum[i]=sum[i-1]+a[i].val;
    memset(dp,-0x3f,sizeof(dp));
    rep(i,1,n)
    {
        if ((i>1) && (g[i][i-1]!=1)) dp[i][i][0]=a[i].val;
        if ((i<n) && (g[i][i+1]!=1)) dp[i][i][1]=a[i].val;
    }
    ll ans=dp[0][0][0];
    rep(len,2,n)
    {
        for (int i=1;i+len-1<=n;i++)
        {
            int j=i+len-1;
            rep(k,i,j)
            {
                ll tmp;
                if (k==i) tmp=dp[i+1][j][0]+sum[j]-sum[i-1];
                else if (k==j) tmp=dp[i][j-1][1]+sum[j]-sum[i-1];
                else tmp=dp[i][k-1][1]+dp[k+1][j][0]+sum[j]-sum[i-1];
                if ((i>1) && (g[k][i-1]!=1)) dp[i][j][0]=max(tmp,dp[i][j][0]);
                if ((j<n) && (g[k][j+1]!=1)) dp[i][j][1]=max(tmp,dp[i][j][1]);
                if (len==n) ans=max(ans,tmp);
            }
        }
    }
    if (ans<0) puts("-1");
    else printf("%lld",ans);
    return 0;
}

T3

30pts应该是暴力枚举区间同时暴力\(check\)\(O(n^3)\)

60pts就是枚举进行旋转区间的最中间的元素(即旋转中心),这样的话可以将一部分\(check\)放到一起,\(O(n^2)\)

满分的话首先考虑到这样一件事情,合法的区间可以被优化到\(O(n)\)级别,因为假设当前有一个旋转区间\([l,r]\),若\(a_l\neq r\)\(a_r\neq l\)的话,我们交换\(l,r\)就是无意义的,于是整个区间都可以往里面缩,最后我们枚举的区间肯定是形如\([i,a_i]\)\([a_i,i]\)

接下来就是优化\(check\),借鉴\(60pts\)的思想,我们把相同旋转中心的区间放在一起,按照长度排序,假设我们当前枚举的旋转区间是\([l,r]\),那么答案就是\(sum(1,l-1)+nowsum(l,r)+sum(r+1,n)\)\(sum(i,j)\)表示\([i,j]\)中的合法点数,注意到\(nowsum\)的值的增加只有可能是因为旋转,假设这是按照长度排序的第\(k\)个区间,那么\(nowsum(i,j)=k\)(即使当前\(l,r\)交换对答案有2的贡献,我们也会在枚举下一个区间的时候考虑到)

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
const int N=10000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define fir first
#define sec second
#define mp(a,b) make_pair(a,b)
#define pb(a) push_back(a)
#define maxd 998244353
#define eps 1e-8
int n,a[100100],pos[100100],sum[100100];
vector<int> rotlen[200200];

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;
}

void solve1()
{
    int sum=0;
    rep(i,1,n) 
    {
        a[i]=read();sum+=(a[i]==i);
    }
    int ans=sum;
    rep(i,1,n)
    {
        int l=i-1,r=i+1,tmp=sum;
        while ((l>=1) && (r<=n))
        {
            if (a[l]==r) tmp++;
            if (a[r]==l) tmp++;
            if (a[l]==l) tmp--;
            if (a[r]==r) tmp--;
            ans=max(tmp,ans);
            l--;r++;
        }
    }
    rep(i,1,n)
    {
        int l=i-1,r=i,tmp=sum;
        while ((l>=1) && (r<=n))
        {
            if (a[l]==r) tmp++;
            if (a[r]==l) tmp++;
            if (a[l]==l) tmp--;
            if (a[r]==r) tmp--;
            ans=max(tmp,ans);
            l--;r++;
        }
    }
    printf("%d",ans);
}

void solve2()
{
    rep(i,1,n)
    {
        a[i]=read();sum[i]=sum[i-1];
        if (a[i]==i) sum[i]++;
    }
    rep(i,1,n)
        rotlen[i+a[i]].pb(abs(i-a[i])+1);
    int ans=sum[n];
    rep(i,1,n*2)
    {
        if (!rotlen[i].size()) continue;
        sort(rotlen[i].begin(),rotlen[i].end());
        int len=rotlen[i].size();
        rep(j,0,len-1)
        {
            int now=rotlen[i][j],l,r;
            if (i%2==0)
            {
                l=i/2-now/2;r=i/2+now/2;
            }
            else
            {
                l=i/2-now/2+1;
                r=i/2+now/2;
            }
            ans=max(ans,j+1+sum[l-1]+sum[n]-sum[r]);
        }
    }
    printf("%d",ans);
}

int main()
{
    //freopen("rotate.in","r",stdin);
    //freopen("rotate.out","w",stdout);
    n=read();
    solve2();
    return 0;
}

转载于:https://www.cnblogs.com/encodetalker/p/11327409.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值