2017-2-14做题记录

这天好像干了很多事。听说有什么节日?不是很懂,反正和我没关系。

 

[9018]1255 Cirno的数列

本校OJ上的题,不过好像没人做,其实还是挺有趣的。

【问题描述】

给定一个长度为n的正整数数列a[i]。

定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。

2种操作(k都是正整数):

1.Modify x k:将第x个数的值修改为k。

2.Query x k:询问有几个i满足graze(x,i)<=k。因为可持久化数据结构的流行,询问不仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为同样的数值,按多次统计)

数据范围比较麻烦,大概就是n和操作都不超过60000,ai最大100000。

思路:不知道跟可持久化到底有什么关系,出题人开心就好。稍微转化一下,每个(i,a[i])当作一个点,支持插入点和询问一个点距离他曼哈顿距离不超过k的有多少个,然后好像变成cdq裸题。cdq分治可以多一个log把插入操作去掉。考虑如何计算曼哈顿距离k以内的点,曼哈顿距离在k以内的点其实构成了一个斜的正方形,可以把整个坐标系旋转45度(实际只要x变成x+y,y变成x-y这样吧),就是询问矩形内有多少点了,把所有点和矩阵按横坐标排序一下,纵坐标上用树状数组之类的维护,矩阵内的点可以拿在矩阵右边的边左边的点减去矩阵左边的边左边的点(好绕),最后只要从左到右扫一遍就好了,总复杂度O(nlogn^2)。其实也可以不要cdq分治,大力二维线段树啊。(我懒)

#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 120000
#define N 262144
int t[MN+5],x[MN+5],y[MN+5],k[MN+5],ans[MN+5],a[MN+5],cnt,T[N*2];
struct P{int p,l,r,f,x;}p[MN*2+5];
bool cmp(P a,P b){return a.p==b.p?a.x<b.x:a.p<b.p;}
void inc(int k,int x){for(k+=N;k;k>>=1)T[k]+=x;}
int query(int l,int r)
{
    int res=0;
    for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1)
    {
        if(~l&1)res+=T[l+1];
        if( r&1)res+=T[r-1];
    }
    return res;
}
void solve(int l,int r)
{
    if(l==r)return;
    int i,mid=l+r>>1;
    for(cnt=0,i=l;i<=mid;++i)if(!t[i])p[cnt++]=(P){x[i]-y[i],x[i]+y[i],0,0,0};
    for(i=mid;++i<=r;)if(t[i])
        p[cnt++]=(P){x[i]-y[i]-k[i]-1,max(x[i]+y[i]-k[i],1),x[i]+y[i]+k[i],-1,i},
        p[cnt++]=(P){x[i]-y[i]+k[i],max(x[i]+y[i]-k[i],1),x[i]+y[i]+k[i],1,i};
    sort(p,p+cnt,cmp);
    for(i=0;i<cnt;++i)
        if(p[i].x)ans[p[i].x]+=p[i].f*query(p[i].l,p[i].r);
        else inc(p[i].l,1);
    for(i=0;i<cnt;++i)if(!p[i].x)inc(p[i].l,-1);
    solve(l,mid);solve(mid+1,r);
}
int main()
{
    int n,m,i;char s[10];
    n=read();m=read();
    for(i=1;i<=n;++i)x[i]=i,y[i]=a[i]=read();
    for(i=n;++i<=n+m;)
    {
        scanf("%s",s);
        if(s[0]=='Q')t[i]=1,y[i]=a[x[i]=read()],k[i]=read();
        else x[i]=read(),y[i]=a[x[i]]=read();
    }
    solve(1,n+m);
    for(i=n;++i<=n+m;)if(t[i])printf("%d\n",ans[i]);
}

 

[USACO 2016 US Open Platinum ]T1. 262144

题目大意:一个长度为N的序列,序列中元素在1…40内,每次可以将两个相邻且相等的数合并成比它们大1的数,求出最大能合并出的数。(N<=262,144)

思路:金组有个248,枚举左端点贪心即可,O(n^2)。考虑DP,f[i][j]表示(j,f[i][j])能合成i,转移方程f[i+1][j]=f[i][f[i][j]+1],不能就记为0,有点类似区间DP,或者倍增?复杂度O(N(maxai+logN))

#include<cstdio>
char B[1<<26],*S=B,C;int X;
inline int read()
{
    while((C=*S++)<'0'||C>'9');
    for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0';
    return X;
}
#define MN 262144
int a[60][MN+5];
int main()
{
    fread(B,1,1<<26,stdin);
    int n=read(),i,j,ans;
    for(i=1;i<=n;++i)a[read()][i]=i;
    for(i=1;i<60;++i)for(j=1;j<=n;++j)if(a[i][j])
    {
        ans=i;
        if(a[i][a[i][j]+1])a[i+1][j]=a[i][a[i][j]+1];
    }
    printf("%d",ans);
}

 

[9018]1948 书架

好像是学长从bzoj还是洛谷上抠下来的,不知道原题号

题目大意:N本书编号为1~N按给定顺序堆在一起,每次可以把一本书放到顶或底,把一本书与前一本或后一本交换,询问编号k在第几本和第k本的编号。N和询问不超过80000。

思路:傻逼平衡树

#include<cstdio>
inline int read()
{
    int x,f=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0';
    return x*f;
}
#define MN 80000
#define rtf MN+1
#define rt c[rtf][0]
int c[MN+5][2],fa[MN+5],s[MN+5];
inline void update(int x){s[x]=s[c[x][0]]+s[c[x][1]]+1;}
void rotate(int x)
{
    int f=fa[x],ff=fa[f],l,r;
    l=c[f][1]==x;r=l^1;
    fa[f]=x;fa[x]=ff;fa[c[x][r]]=f;
    c[ff][c[ff][1]==f]=x;c[f][l]=c[x][r];c[x][r]=f;
    update(f);
}
void splay(int x,int r)
{
    for(int f;(f=fa[x])!=r;rotate(x))
        if(fa[f]!=r)rotate(c[fa[f]][0]==f^c[f][0]==x?x:f);
    update(x);
}
int get(int f,int t){while(c[f][t])f=c[f][t];return f;}
inline void ins(int f,int t,int x){fa[c[f][t]=x]=f;}
void del(int x)
{
    splay(x,rtf);
    if(c[x][0])
    {
        splay(get(c[x][0],1),rt);
        ins(rtf,0,c[x][0]);
        ins(c[x][0],1,c[x][1]);
        update(c[x][0]);
    }
    else ins(rtf,0,c[x][1]);
    c[x][0]=c[x][1]=0;
}
int getk(int x,int k)
{
    if(k<=s[c[x][0]])return getk(c[x][0],k);
    if(k-=s[c[x][0]]+1)return getk(c[x][1],k);
    return x;
}
int main()
{
    int n,m,i,x;char t[10];
    n=read();m=read();
    ins(rtf,0,read());for(i=2;i<=n;++i)ins(get(rt,1),1,x=read()),splay(x,rtf);
    while(m--)
    {
        scanf("%s",t);x=read();
        if(t[0]=='T')del(x),ins(get(rt,0),0,x),splay(x,rtf);
        if(t[0]=='B')del(x),ins(get(rt,1),1,x),splay(x,rtf);
        if(t[0]=='I')
        {
            if(!(i=read()))continue;
            splay(x,rtf);
            if(i>0)splay(get(c[x][1],0),rtf),x=rt;
            del(x);
            if(!c[rt][0])ins(rt,0,x);else ins(get(c[rt][0],1),1,x);splay(x,rtf);
        }
        if(t[0]=='A')splay(x,rtf),printf("%d\n",s[c[x][0]]);
        if(t[0]=='Q')splay(getk(rt,x),rtf),printf("%d\n",rt);
    }
}

 

转载于:https://www.cnblogs.com/ditoly/p/20170214P.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值