cf_710_E

转移自老blog

cf_710_E

此文更新于2019.6.22
你最开始有一个0,要构造出n
你有两种操作,
    1 加上或减去一 代价为x
    2 乘以2 代价为y
数据范围
    n<1e18
    x<1e9
    y<1e9
dp[i] -> 构造出n的最小代价
i为偶数 dp[i] <- dp[i-1] , dp[i/2]
i为奇数 dp[i] <- dp[i-1] , dp[i/2+1]
// #include
// using namespace std;

// typedef long long ll;
// const ll maxn=2e7+7;
// ll d[maxn];

// struct node{
// ll n,step;
// bool operator <(const node&rhs) const{
// return step>rhs.step;
// }
// };

// int main(){
// ll n,x,y;
// scanf("%lld%lld%lld",&n,&x,&y);
// for(ll i=0;i<=2*n;i++) d[i]=1e18;
// priority_queue q;
// d[1]=x;
// q.push(node{1,x});

// ll ans=x*n;
// while(true){
// node top=q.top(); q.pop();
// ans=min(ans,d[]+);
// if(top.n==n) break;
// else{
// if(top.n-1>=1&&d[top.n]+x
// d[top.n-1]=d[top.n]+x;
// q.push(node{top.n-1,d[top.n]+x});
// }
// if(top.n+1<=2*n&&d[top.n]+x
// d[top.n+1]=d[top.n]+x;
// q.push(node{top.n+1,d[top.n]+x});
// }
// if(top.n*2<=2*n&&d[top.n]+y
// d[top.n*2]=d[top.n]+y;
// q.push(node{top.n*2,d[top.n]+y});
// }
// }
// }
// cout<
// }



#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll maxn=2e7+7;
ll dp[maxn];

int main(){
    ll n,x,y;
    scanf("%lld%lld%lld",&n,&x,&y);
    for(int i=1;i<=n;i++){
        if(i&1){//odd
            dp[i]=min(dp[i-1]+x,dp[(i+1)/2]+y+x);
        }
        else{
            dp[i]=min(dp[i-1]+x,dp[i/2]+y);
        }
    }
    cout<<dp[n]<<endl;
}














此文标签
dp优化转移方程

牛客18多校第一场H

转移自老blog

牛客18多校第一场H

此文更新于2019.7.12
给一颗边带权点不带权点树
定义两点间的距离为:
    若路径上的边权构成数列a[0...n]
    则距离d= 对所有i>=1求和 (a[i-1]-a[i])*(a[i-1]-a[i])
现在要求距离每个点最远的那个点的距离为多少
树dp求down很简单
关于up , 仔细分析,抽象出来为此:
给你w[]和d[]
对每一个i,要求出最大化j!=i时的d[j]+(w[i]-w[j])
化简后发现为
d[j]+w[i]*w[i]+w[j]*w[j]-2*w[i]*w[j]
= w[i]*w[i] + (-2*w[i])*w[j] + (d[j]+w[j]*w[j])
显然斜率优化,我们排序后对前缀和后缀做两遍斜率优化即可
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

#define pwx2(x) ((x)*(x))

const ll maxn=1e5+5;
struct star{ll v,w,nex;}edge[maxn<<1];
ll head[maxn],cnt,n;
ll down[maxn],up[maxn],ans[maxn];
struct line{ll k,b,id;};// y=kx+b
double cross(line l1,line l2){// k1x+b1=k2x+b2 -> k1x-k2x=b2-b1
    return -double(l1.b-l2.b)/(l1.k-l2.k);
}
ll val_at(line l1,ll x){
    return x*l1.k+l1.b;
}

void ini(ll _n){
    n=_n;
    cnt=-1;
    for(ll i=0;i<=n;i++head[i]=-1,down[i]=up[i]=ans[i]=0;
}

void add_edge(ll u,ll v,ll w){
    edge[++cnt]=star{v,w,head[u]}; head[u]=cnt;
    edge[++cnt]=star{u,w,head[v]}; head[v]=cnt;
}

void dfs1(ll u,ll father=0,ll w=0){
    for(ll i=head[u];~i;i=edge[i].nex){
        if(edge[i].v==fathercontinue;
        dfs1(edge[i].v,u,edge[i].w);
        if(father) {
            down[u]=max(down[u],down[edge[i].v]+pwx2(w-edge[i].w));
            ans[father]=max(ans[father],down[u]);
        }
    }
}

void dfs2(ll u,ll father=0){
    vector<linevec;
    for(ll i=head[u];~i;i=edge[i].nex){
        ll v=edge[i].v,w=edge[i].w;
        if(v==fathervec.push_back(line{-2*w,up[u]+w*w,i});
        else vec.push_back(line{-2*w,down[v]+w*w,i});
    }
    sort(vec.begin(),vec.end(),[](line l,line r){return l.k<r.k;});
    static line stk[maxn];
    ll tot=0;
    for(ll i=0;i<vec.size();i++){// k++ x--
        star&e=edge[vec[i].id];
        while(tot>=2&&cross(stk[tot],stk[tot-1])>e.w)tot--;
        if(e.v!=father&&tot>=1) {
            star&e2=edge[stk[tot].id];
            up[e.v]=max(up[e.v],val_at(stk[tot],e.w)+e.w*e.w);
            ans[e.v]=max(ans[e.v],up[e.v]);
        }
        if(tot>=1&&vec[i].k==stk[tot].k){
            if(vec[i].b>stk[tot].b)tot--;
            else continue;
        }
        while(tot>=2&&cross(vec[i],stk[tot])<cross(stk[tot],stk[tot-1]))tot--;
        stk[++tot]=vec[i];
    }
    tot=0;
    for(ll i=int(vec.size())-1;i>=0;i--){// k--
        star&e=edge[vec[i].id];
        while(tot>=2&&cross(stk[tot],stk[tot-1])<e.w)tot--;
        if(e.v!=father&&tot>=1) {
            star&e2=edge[stk[tot].id];
            up[e.v]=max(up[e.v],val_at(stk[tot],e.w)+e.w*e.w);
            ans[e.v]=max(ans[e.v],up[e.v]);
        }
        if(tot>=1&&vec[i].k==stk[tot].k){
            if(vec[i].b>stk[tot].b)tot--;
            else continue;
        }
        while(tot>=2&&cross(vec[i],stk[tot])>cross(stk[tot],stk[tot-1]))tot--;
        stk[++tot]=vec[i];
    }
    for(ll i=head[u];~i;i=edge[i].nex){
        if(edge[i].v==fathercontinue;
        dfs2(edge[i].v,u);
    }
}

int main(){
    ll n;
    while(~scanf("%lld",&n)){
        ini(n);
        for(ll i=0;i<n-1;i++){
            ll u,v,w;
            scanf("%lld%lld%lld",&u,&v,&w);
            add_edge(u,v,w);
        }
        dfs1(1);
        dfs2(1);
        for(ll i=1;i<=n;i++printf("%lld\n",ans[i]);
    }
}

此文标签
树形dp 斜率优化dp

about_sqrt_and_acos

转移自老blog

关于sqrt对负数开根号acos对大于1的数计算反三角函数值的问题

空间里面有一个实心球,两个点,问两点不经过实心球的路径的最小值.
题目意思很简单,我们可能会用到余弦定理,但是,余弦定理有误差,我们可能会得到一些奇怪的数字,浮点数的误差导致 余弦定理计算出来了小于-1或者大于1的其他数字,当我们对这样的数字进行反三角函数运算时,会得到nan,当然sqrt 有时候也会碰到对负数开方,于是我们要重写这两个函数
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-12;
const double INF = 1e18;
const double PI = acos(-1.0);
double ACOS(double x)
{
    if(x>1)
        x=1.0;
    if(x<-1)
        x=-1.0;
    return acos(x);
}
double SQRT(double x)
{
    if(x<0)
        return 0;
    return sqrt(x);
}

struct Point3D
{
    double x, y, z;
    Point3D() {};
    Point3D(double xx, double yy,double zz)
    {
        x = xx;
        y = yy;
        z = zz;
    }
};
typedef struct Point2D
{
    Point2D() {};
    double x;
    double y;
    Point2D(double xx, double yy)
    {
        x = xx;
        y = yy;
    }
} Point;

struct Circle3D
{
    Point3D o;
    double r;
};
struct Circle
{
    Point2D o;
    double r;
};


struct Line
{
    Point s, e;
    Line() {};
    Line(Point a, Point b)
    {
        s = a;
        e = b;
    };
};


double dis(Point a, Point b)
{
    return SQRT(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}


double dis(Point3D a, Point3D b)
{
    return SQRT(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) + pow(a.z - b.z, 2));
}

double solve(Point3D s, Point3D t, Circle3D o)
{
    double so=dis(s,o.o);
    double to=dis(t,o.o);
    double st=dis(s,t);
    double sita_sot=ACOS((so*so+to*to-st*st)/2/so/to);
    double sita_sto=ACOS((st*st+to*to-so*so)/2/st/to);
    double sita_tso=ACOS((st*st+so*so-to*to)/2/st/so);


    double p=(so+to+st)/2;
    double d=SQRT(p*(p-so)*(p-to)*(p-st))*2/st;


    if(sita_sto<PI/2&&sita_tso<PI/2&&d<o.r)
    {
        double sita_soa1=ACOS(o.r/so);
        double sita_toa2=ACOS(o.r/to);

        double sita=sita_sot-sita_soa1-sita_toa2;

        return sita*o.r + so*sin(sita_soa1) + to*sin(sita_toa2);

    }
    else
    {
        return dis(s, t);
    }
}


double solve(Point s, Point t, Circle o)
{
    double so=dis(s,o.o);
    double to=dis(t,o.o);
    double st=dis(s,t);
    double sita_sot=ACOS((so*so+to*to-st*st)/2/so/to);
    double sita_sto=ACOS((st*st+to*to-so*so)/2/st/to);
    double sita_tso=ACOS((st*st+so*so-to*to)/2/st/so);


    double p=(so+to+st)/2;
    double d=SQRT(p*(p-so)*(p-to)*(p-st))*2/st;


    if(sita_sto<PI/2&&sita_tso<PI/2&&d<o.r)
    {
        double sita_soa1=ACOS(o.r/so);
        double sita_toa2=ACOS(o.r/to);

        double sita=sita_sot-sita_soa1-sita_toa2;

        return sita*o.r + so*sin(sita_soa1) + to*sin(sita_toa2);

    }
    else
    {
        return dis(s, t);
    }
}
double solve(double a,double b,double c,double r)
{
    Point s, t;
    Circle o;
    double arcsita = (pow(b, 2) + pow(c, 2) - pow(a, 2)) / 2 / b / c;
    double sita = ACOS(arcsita);

    t.x = c * cos(sita);
    t.y = c * sin(sita);

    o.o.x = 0;
    o.o.y = 0;
    o.r = r;

    s.x = b;
    s.y = 0;

    return solve(s, t, o);
}


int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        Circle3D o;
        Point3D s, t;
        scanf("%lf%lf%lf%lf", &o.o.x, &o.o.y, &o.o.z,&o.r);
        scanf("%lf%lf%lf", &s.x, &s.y, &s.z);
        scanf("%lf%lf%lf", &t.x, &t.y, &t.z);
        double a = dis(s, t);
        double b = dis(s, o.o);
        double c = dis(t, o.o);

        if(fabs(a)<eps)
        {
            printf("0.0000000000\n");
        }
        else
        {
            printf("%.8lf\n", solve(a,b,c,o.r));
        }
    }
}

bzoj2124

转移自老blog

bzoj2124

题目大意 :
       在一个很长(1e5)的串里面找一个长度为三的等比子序列
         考虑枚举中间值,想办法在lg的复杂度下解决单次枚举,初次考虑为依次比较中间值+d和中间值-d是否出现在这个值的前面和后面 这里可以通过映射On完成,但是复杂度仍然达不到要求,然后我们考虑到这是一个排列,也就是说那些数字出现且仅出现一次,那我 们用一个01数列来维护某个值是否出现在枚举值的前面和后面,这里假设出现在前面为0,后面为1,那什么时候最终答案是yes呢,当 且仅当关于某个中间值+d和这个中间值-d在01数列中恰好一个为0一个为1,也就是说不相同,这里还是很复杂,我们考虑把01数列当作 一个大数,那答案是yes就意味着这个大数关于中间值没有局部回文,好!现在我们暂停深入分析,总结一下,首先我们在枚举中间值 构建01数列,然后我们在01数列中寻找关于中心值的局部回文是否不存在。再进一步分析:枚举中间值的过程中,01数列在做单点修改 查询局部回文是否不成立的过程可以直接用字符串hash解决,题目到此已经解决。解法:线段树维护01数列正反hash值,如何正反?构 造一个反向01数列,用俩线段树就行了。
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

#define ls (u<<1)
#define rs (u<<1|1)
#define ml ((l+r)>>1)
#define mr (ml+1)
struct segment_tree{
    const static ll maxn=2.1e4;
    const static ll MOD=1e9+7;
    static ll pow_2[maxn];
    ll n;
    ll tree[maxn<<2];

    static void pow_ini(){
        pow_2[0]=1;
        for(int i=1;i<maxn;i++){
            pow_2[i]=pow_2[i-1]*2;
            if(pow_2[i]>MOD)pow_2[i]-=MOD;
        }
    }


    void pushup(ll l ,ll r,ll u){
        tree[u]=(tree[ls]*pow_2[r-((l+r)>>1)]+tree[rs])%MOD;
    }
    void build(ll nn){
        n=nn;
        build(1,n,1);
    }


    void build(ll l,ll r,ll u){
        if(l==r){
            tree[u]=0;
            return ;
        }

        build(l,ml,ls);
        build(mr,r,rs);
        pushup(l,r,u);
    }
    void update(ll q,ll d){
        update(q,d,1,1,n);
    }
    void update(ll q,ll d,ll u,ll l,ll r){
        if(q<=l&&r<=q){
            tree[u]=(tree[u]+d*pow_2[r-q])%MOD;
            return ;
        }
        if(q<=ml)update(q,d,ls,l,ml);
        if(q>=mr)update(q,d,rs,mr,r);
        pushup(l,r,u);
    }
    ll query(ll ql,ll qr){
        return query(ql,qr,1,1,n)%MOD;
    }
    ll query(ll ql,ll qr,ll u,ll l,ll r){
        if(ql<=l&&r<=qr)return tree[u]*pow_2[qr-r]%MOD;
        ll ret=0;
        if(ql<=ml)ret=ret+query(ql,qr,ls,l,ml);
        if(qr>=mr)ret=ret+query(ql,qr,rs,mr,r);
        return ret;
    }
}t1,t2;

ll segment_tree::pow_2[maxn];

const int maxn=segment_tree::maxn;
int per[maxn];


int main(){

    segment_tree::pow_ini();

    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)scanf("%d",per+i);

        t1.build(n);
        t2.build(n);

        string ans="N";
        for(int mid=1;mid<n-1;mid++){
            t1.update(per[mid-1],1);
            t2.update(n+1-per[mid-1],1);

            int len=min(per[mid]-1,n-per[mid]);
            if(len==0)continue;

            if(t1.query(per[mid]-len,per[mid]-1)!=t2.query(      n+1-(per[mid]+len)   ,   n+1-(per[mid]+1) )){
                ans="Y";
                break;
            }
        }
        cout<<ans<<endl;
    }
}

ACM-ICPC 2018 徐州赛区网络预赛 G Trace

转移自老blog

ACM-ICPC 2018 徐州赛区网络预赛 G. Trace

题目大意 :
       有n波海浪,每个海浪是一个以(0,0)为左下角(x,y)为右上角的长方形,每一波海浪会留下自己 的长方形边界为海浪残留,同时会冲刷掉长方形内部的其他海浪残留,问n波之后,留下来的海浪边界总长度为多少n<50000
               逆序考虑海浪,维护两个函数 col_limit(x)和row_limit(y) 分别表示第x列的前col_limit(x)行的残留会被后面的海浪冲刷掉,第y行的前row_limit(y)列的残留会被后面的冲刷掉。 我们可以根据这两个函数转移出第i个海浪对答案的贡献,然后我们考虑,如何更新出i-1下的正确col_limit函数和row_limit函数,容易发现,这是一个区间取最值操作。
               现在来总结一下,对于这两个函数,我们有两种操作: 区间取最值,点查询。-> 用线段树维护即可
#include<bits/stdc++.h>
using namespace std;

#define ml ((l+r)>>1)
#define mr (ml+1)
struct seg_tree{// 5e4*50*4=5e4*200=1e7
    static const int maxn=5e4+5;
    int mxx[maxn*2*25],lz[maxn*2*25],ls[maxn*2*25],rs[maxn*2*25],tot;

    void push_son(int&son,int l,int r,int lzrt){
        if(son==0) {
            son=++tot;
            mxx[son]=0;
            lz[son]=-1;
            ls[son]=0;
            rs[son]=0;
        }
        if(lzrt!=-1){
            mxx[son]=max(mxx[son],lzrt);
            lz[son]=max(lz[son],lzrt);
        }
    }
    void push_down(int rt,int l,int r){
        push_son(ls[rt],l,ml,lz[rt]);
        push_son(rs[rt],mr,r,lz[rt]);
        lz[rt]=-1;
    }
    void push_up(int rt,int l,int r){
        // nothing
    }
    void build(int rt,int l,int r){//1 1 n
        rt=tot=0;
        push_son(rt,l,r,0);
    }
    void update(int rt,int l,int r,int ql,int qr,int d){
        if(ql<=l&&r<=qr){
            push_son(rt,l,r,d);
        }
        else{
            push_down(rt,l,r);
            if(ml>=ql) update(ls[rt],l,ml,ql,qr,d);
            if(mr<=qr) update(rs[rt],mr,r,ql,qr,d);
            push_up(rt,l,r);
        }
    }
    int query(int rt,int l,int r,int q){
        if(l==r){
            return mxx[rt];
        }
        else{
            push_down(rt,l,r);
            if(ml>=q) return query(ls[rt],l,ml,q);
            else return query(rs[rt],mr,r,q);
        }
    }
}row,col;
//2e7*32bit=2e7*4b=2e4*4kb=20*4mb=80mb   ok

const int maxn=5e4+5;
int x[maxn],y[maxn];
int main(){
    int n;
    scanf("%d",&n);
    row.build(1,1,1e7+5);
    col.build(1,1,1e7+5);
    for(int i=1;i<=n;i++) scanf("%d%d",x+i,y+i);
    long long ans=0;
    for(int i=n;i>=1;i--){
        int rowlimit=row.query(1,1,1e7+5,y[i]);
        int collimit=col.query(1,1,1e7+5,x[i]);
        if(x[i]>rowlimit) ans+=x[i]-rowlimit;
        if(y[i]>collimit) ans+=y[i]-collimit;
        row.update(1,1,1e7+5,1,y[i],x[i]);
        col.update(1,1,1e7+5,1,x[i],y[i]);
    }
    cout<<ans<<endl;
}

hdu5726

转移自老blog

hdu5726

题目难点 :
        给你一个不变的序列,输入很多k,输出区间gcd等于k的区间的数量。
对所有区间计数,枚举左端点,二分右端点即可。
理论依据: 以任意左端点为起点的所有区间的gcd的种类不会超过这个点的值的因子的个数。
细节: 区间询问使用ST表.
#include<bits/stdc++.h>
using namespace std;
#define ll long long

const ll maxn=1e5+5;
map<ll,ll>mp;
ll t,n,q;
ll a[maxn];

ll mm[maxn],rmq[maxn][20];

void st(ll l,ll r){
    mm[0]=-1;
    for(ll i=1;i<maxn;i++){
        mm[i]=((i&i-1)==0)?mm[i-1]+1:mm[i-1];
    }//如果卡常,可以放外面
    for(ll i=l;i<=r;i++)rmq[i][0]=a[i];
    for(ll j=1;j<=mm[r-l+1];j++)
        for(ll i=l;i+(1<<j)-1<=r;i++)
            rmq[i][j]=__gcd(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
}
ll query(ll l,ll r){
    ll k=mm[r-l+1];
    return __gcd(rmq[l][k],rmq[r-(1<<k)+1][k]);
}


void ini()
{
    for(ll i=1; i<=n; i++)
    {
        ll l,r=i-1;

       // cout<<i<<":";
       ///100
        while(r<n){//  r : gcd[i..r]!=gcd[i,r+1]
           // cout<<r<<" ";
            ll pre_r=r;
            l=r+2,r=n+1;
            ll gcd=query(i,l-1);
            ///lg
            while(l<r){
                ll mid=(l+r)>>1;
                ///lg
                if(mid>n||gcd!=query(i,mid)){
                    r=mid;// query(i,r)!=gcd
                }
                else l=mid+1;//query(i,l-1)=gcd
            }// query(i,l-1)=gcd&&query(i,l)!=gcd
            mp[gcd]+=(l-1)-(pre_r+1)+1;
            r=l-1;
        }
      //  cout<<endl;
    }

}


int main()
{
    scanf("%lld",&t);
    for(ll time=1; time<=t; time++)
    {
        mp.clear();
        scanf("%lld",&n);
        for(ll i=1;i<=n;i++)scanf("%lld",a+i);

        st(1,n);
        ini();


        scanf("%lld",&q);
        printf("Case #%lld:\n",time);
        for(ll i=1; i<=q; i++)
        {
            ll l,r;
            scanf("%lld %lld",&l,&r);
            ll ans=query(l,r);
            printf("%lld ",ans);
            printf("%lld\n",mp[ans]);
        }
    }
}

hdu6183

转移自老blog

hdu6183   Color   it

题目大意 :
       D喜欢画画,为了防止他画太乱的画,D要你帮他维护一些操作,
        0:清除所有的颜色
        1 x y c:在点(x,y)添加颜色c
        2  x  y1  y2:问矩形(1,y1)->(x,y2)中有多少种颜色
因为询问中x方向上的起点都是1,且只询问是存在性询问,所以我们贪心地维护每一行(yi)的每一种颜色出现的最小横坐标。
这样做时空复杂度都为50*n*logn
时空都不符合要求
对于时间:
剪枝1:我们在左右递归的时候,如果发现左子树存在解,则结束递归,不去搜索右子树。
剪枝2:维护区间最小值,当区间最小值大于询问值时,结束递归(此操作同时优化了空间)。
#include<bits/stdc++.h>
using namespace std;

#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn=15e4+5;
int mii[maxn*2*25],rs[maxn*2*25],ls[maxn*2*25],tot;  // 90mb

void ini(){
    tot=0;
}
void push_son(int&son,int l,int r){
    if(son==0){
        son=++tot;
        ls[son]=0;
        rs[son]=0;
        mii[son]=0x7fffffff;
    }
}
void push_down(int rt,int l,int r){
    push_son(ls[rt],l,ml);
    push_son(rs[rt],mr,r);
}
void push_up(int rt,int l,int r){
    mii[rt]=min(mii[ls[rt]],mii[rs[rt]]);
}
void build(int&rt,int l,int r){//1 1 n
    rt=0;
    push_son(rt,l,r);
}
void update(int rt,int l,int r,int q,int d){
    if(l==r){
        mii[rt]=min(mii[rt],d);
    }
    else{
        push_down(rt,l,r);
        if(ml>=q) update(ls[rt],l,ml,q,d);
        else update(rs[rt],mr,r,q,d);
        push_up(rt,l,r);
    }
}
int query(int rt,int l,int r,int ql,int qr,int x){
    if(mii[rt]>x) return 0;
    if(ql<=l&&r<=qr){
        return 1;
    }
    else{
        push_down(rt,l,r);
        if(ml>=ql) if(query(ls[rt],l,ml,ql,qr,x)) return 1;
        if(mr<=qr) if(query(rs[rt],mr,r,ql,qr,x)) return 1;
        return 0;
    }
}

inline int read(){
    int ret=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while('0'<=ch&&ch<='9') ret=(ret<<1)+(ret<<3)+(ch^48),ch=getchar();
    return ret;
}

int rt[55];
int main(){
    while(true){
        int op=read();
        if(op==0){
            ini();
            for(int i=0;i<=50;i++) build(rt[i],1,1e6+5);
        }
        else if(op==1){
            int x,y,c;
            x=read();y=read();c=read();
            update(rt[c],1,1e6+5,y,x);
        }
        else if(op==2){
            int x,y1,y2;
            x=read();y1=read();y2=read();
            int ans=0;
            for(int i=0;i<=50;i++) ans+=query(rt[i],1,1e6+5,y1,y2,x);
            printf("%d\n",ans);
        }
        else break;
    }
}

hdu5745

转移自老blog

hdu5726

题目大意 :
         在母串里面找子串,字串可以变化,询问所有匹配位置
        其实是一个dp,设状态dp[i][j][0]表示母串匹配到i子串匹配到 j,且子串最后一个字符与前面的字符交换, dp[i][j][1]不交换,dp[i][j][2]交换, 于是就有了转移式子:
         dp[j][0][i] = dp[j-1][2][i-1]&&s[i]==p[j-1];
         dp[j][1][i] = (dp[j-1][1][i-1]||dp[j-1][0][i-1]) && s[i]==p[j];
         dp[j][2][i] = (dp[j-1][1][i-1]||dp[j-1][0][i-1]) && s[i]==p[j+1];
然后bitset暴力压缩掉一维,滚动数组又压缩掉一个维度。
要注意biset压缩掉大的那一维度,滚动数组压缩掉第二大的维度才能过,压反了就tle了。
#include<bits/stdc++.h>
using namespace std;

const int MAXS=1e5+5,MAXP=5e3+5;
char s[MAXS],p[MAXP];
bitset<MAXS> dp[2][3];
bitset<MAXS> s_char[26];


int main(){
    int n,m,T;
    scanf("%d",&T);
    while(T--){
        scanf("%d %d %s %s",&n,&m,s,p);
        for(int i=0;i<26;i++)s_char[i].reset();
        for(int i=0;i<n;i++)s_char[s[i]-'a'][i]=1;
        
        
        int t=1;
        
        dp[t][0][0]=0;
        dp[t][1][0]=s[0]==p[0];
        dp[t][2][0]=s[0]==p[1];
        for(int i=1;i<n;i++){
            dp[t][0][i]=0;
            dp[t][1][i]=s[i]==p[0];
            dp[t][2][i]=s[i]==p[1];
            }
        t^=1;
        
        for(int j=1;j<m;j++){
            
            dp[t][0] =  (dp[t^1][2]<<1) & s_char[p[j-1]-'a'];
            dp[t][1] = ( (dp[t^1][1]|dp[t^1][0]) << 1 ) & s_char[p[j]-'a'];
            if(j+1<m)dp[t][2] = ((dp[t^1][1]|dp[t^1][0])<<1) & s_char[p[j+1]-'a'];
            else dp[t][2].reset();
            
            dp[t][0][0] = 0;
            dp[t][1][0] = 0;
            dp[t][2][0] = 0;
            
            t^=1;
            }
        
        
        
        for(int i=m-1;i<n;i++){
            printf("%d",(dp[t^1][0][i]|dp[t^1][1][i]));
            }
        for(int i=0;i<m-1;i++){
            printf("0");
            }
        cout<<endl;
        }
    }
//000001
//0000011
<

hdu6161

转移自老blog

hdu6161

题目大意 :
         给你一颗很大的完全二叉树,节点编号从1到n,对于除了1号节点以外的其他节点x,他的父亲是x>>1,1号节点为根,节点x的初始权值为x
给出两种操作:
1.update u x 意味着更新节点u的权值为x
2.query u 询问经过节点u的路径中,权值最大的条路径的权,(定义路径的权为路径上节点的权的和)
操作一共有m次
数据范围:n<1e8,m<1e5,x<1e10 时间:2s

        最开始考虑到这是一棵很大的树,我们不可能把这棵树建出来,太大了,空间肯定不够,注意到操作次数只有1e5,然后研究什么是最大路径的权?我们考虑任意一个节点u,他有三条边分别连向父亲fa,左儿子lson,右儿子rson,如果我们处理了以这三个点为起点且不经过u的路径的最值,那么我们就可以通过这三个数据转移过来。
        再考虑到这棵树是有规律的,节点数远大于操作数,那么我们依据各种高级数据结构中常用的懒惰标记(懒修改)来优化空间复杂度,来假装建立了这棵树,我们访问哪个节点,我们就为哪个节点开辟空间,其他的我们设置懒惰标记,表示此节点,以及他的整颗子树都被放置懒标记。
        处理完了空间以及询问操作,下一步是修改,当我们发现,我们修改了某个节点的值后,他以及他的祖先的向下的路径值都可能发生变化,这些节点的数量级是lg的,能够忍受,但是,这个节点的所有子孙向上的路径最值也有可能发生变化,这写节点的数量级是非常大的,远超线性。此时不能忽视,到此已经无解。
        看过题解以后,发现自己的解法离正解已经很接近了,首先第一点,正解说用map来储存树结构,这样子就不必去储存边,且大大优化了编码难度,然而却牺牲了时间,因为我们如果通过指针建树的话,从一个节点走向另一个节点可以O(1)办到,但是用map不行,map在这个基本操作上是O(lg)级别的,这就导致用map的话最终时间复杂度多一个lg。但是可以证明,多此lg,依旧可以过题,时间不会超。
        第二点,状态的选取:dp[x]代表以x为根的子树中以x为起点的路径的权的最大值。这个状态要好很多,相比我设的三个状态,第一他状态简单,第二单次修改的转移时间复杂度O(lgn),因为他少一个向上的路径的状态,根本就不需要这个状态。
        修改权值的转移方程:
        从下至上,若修改节点x,且father为x的父亲,brother为x的兄弟,lson为x的左儿子,rson为x的右儿子,w代表点权,dp代表状态值,则dp[x]=max(dp[lson],dp[rson])+w[x],
        dp[father]=max(dp[x],dp[brother])+w[father]。。。按此方法一直更新到根即可。
        询问路径的转移方程:
        依旧从下至上。肯定要去三个值的,x往左儿子的dp[x <<1],x往右儿子的dp[x<<1|1],然而,取不到父亲的值了,因为我们就没有维护这个状态值,不过还是有办法来得到它,我们考虑x往父亲的方向的最大路径值,这其实也可以递归下去,我们设这种值叫做up,利用动态规划,自顶向下搜索:x往父亲的权,在父亲那里只有两个方向可以走,因为是从x过来的,肯定不能回去的,那就少了一个方向,如果父亲继续向上走,我们可以递归下去,如果父亲向下走,那就只能走兄弟方向的路径,决策出现了:up[x]=w[x]+max(up[father],dp[brother])
        至此,更新和询问都已经解决,一个自底向上递归转移,一个自顶向下搜索转移。
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

struct mydata{
    struct node{
        ll w,dp;
        node():w(0),dp(0){}
        node(ll w,ll dp):w(w),dp(dp){}
    };
    map<ll,node>tree;
    ll n;

    void ini(ll n){
        this->n=n;
        tree.clear();
    }

    inline node get(ll x){
        if(x>n)return node(0,0);

        node ret=tree[x];
        if(ret.w!=0)return ret;

        ll l=x,r=x;
        while(l<=n)l=l<<1;
        while(r<=n)r=r<<1|1;
        l>>=1;
        r>>=1;

        ll big=l<=r?r:n;
        ll dp=0;
        while(big>=x){
            dp+=big;
            big>>=1;
        }
        return tree[x]=node(x,dp);
    }

    void update(ll x,ll w){
        node l=get(x<<1);//lg
        node r=get(x<<1|1);//lg
        tree[x]=node(w,max(l.dp,r.dp)+w);//lg
        if(x==1)return;
        else update(x>>1,get(x>>1).w);
    }//lglg

    ll query(ll x){
        ll fa=max_sum_up(x);
        ll l=get(x<<1).dp;
        ll r=get(x<<1|1).dp;
        return fa+l+r-min(min(l,r),fa)+get(x).w;
    }

    ll max_sum_up(ll x){
        if(x==1)return 0;
        return get(x>>1).w+max(max_sum_up(x>>1),get(x^1).dp);
    }
}tree;

int main(){

    char str[10];
    ll n,m,u,x;
    while(~scanf("%lld%lld",&n,&m)){
        tree.ini(n);

        for(ll i=0;i<m;i++){
            scanf("%s",str);
            if(str[0]=='q'){
                scanf("%lld",&u);
                printf("%lld\n",tree.query(u));
            }
            else{
                scanf("%lld%lld",&u,&x);
                tree.update(u, x);
            }
        }
    }
}

hdu6170

转移自老blog

hdu6170

题目大意 :
         类似正则匹配

         这个题开始的时候没有写出来,想到了状态的设法,如果设dp[i][j]代表第一个串的前i项能否匹配第二个串的前j项,状态个数很明显是n2的,然而一直苦于*符号的匹配,找不到好的转移方程,不管怎么想都是n3的转移复杂度,当s2[j]是*的时候,dp[i][j] <-dp[pre][j] 其中pre可以取的值的要求是:对于所有的pre<=k<=i s1【k】=s1【i】
         这个转移不是不可以优化,这显然是一个连续的区间,可以采取rmq预处理来实现降低时间复杂度到n2lgn,但是这样写似乎显得小题大做,并且时间复杂度不是很理想,可能卡在时间的边界上然后time limited。
         最后看了题解,发现可以O(1)转移,但是网上的题解的证明过程显得苍白无力,甚至有不少代码是错的,ac不过是因为数据弱而已。
         我想了很久,最终得到了*的转移: 考虑s1[i]和s1[i-1]是否相等,如果不相等的话,那么*要么贡献0次由dp[i][j-2]转移,要么贡献1次由dp[i][j-1]转移,复杂的是相等的情况,*贡献0次和1次是一样的,因为s1[i]和s1[i-1]相等,所以*可能贡献多次(多指的是大于等于二)。
         分析贡献多次的时候:从dp[i][j]=1必要条件入手,因为多次,所以如果他少贡献一次的话,dp[i-1][j]也为1,且有条件s1[i-1]=s2[j-1]或s2[j-1]=='.'。
         然后证明此必要条件的充分性,由于dp[i-1][j]=1且s1[i-1]和s2[j-1]是一样的,那么*多贡献一次就可以了。因为s1[i-1]和s2[j-1]是一样的,所以可以多贡献一次。
         于是(dp[i-1][j]=1且s1[i-1]和s2[j-1]一样)与(贡献多次的dp[i][j]=1)等价。
         这前的每一个细节都很重要,为什么要求s1[i-1]和s2[j-1]是一样的,如果不做此要求,就过不了这个例子:
aa
ab*
         至于dp边界的处理,特别简单,我们改变输入数据s1 和s2为(“@@”+s1)和(“@@”+s2),边界就特别特别简单了。
         虽说简单,但我还是搞错了,忽略了一种情况因为少了下面的代码,
         然后就过不了这个反例
c
a*b*c
         因为什么呢?因为”@@”居然可以和”@@a*”匹配,简直是恐怖。
#include<bits/stdc++.h>
using namespace std;

int main(){
    int T;
    scanf("%d ",&T);
    while(T--){
        static const int maxn=2600;
        static char s1[maxn],s2[maxn];
        static int dp[maxn][maxn];
        //scanf("%s%s",s1+2,s2+2);
        gets(s1+2);
        gets(s2+2);
        s1[0]=s2[0]=s1[1]=s2[1]='@';
        int l1=strlen(s1);
        int l2=strlen(s2);
        dp[0][0]=dp[1][1]=1;
        for(int j=2;j<l2;j++){
            if(s2[j]=='*')dp[1][j]=dp[1][j-2];
            else dp[1][j]=0;
        }

        for(int i=2;i<l1;i++){
            for(int j=2;j<l2;j++){
                if(s2[j]=='.')dp[i][j]=dp[i-1][j-1];
                else if(s2[j]=='*'){
                    if(s1[i]!=s1[i-1]){//最多贡献一次
                        dp[i][j]=dp[i][j-2]||dp[i][j-1];
                    }
                    else{
                        dp[i][j]=dp[i][j-2]||dp[i][j-1]||(dp[i-1][j]&&(s1[i-1]==s2[j-1]||s2[j-1]=='.'));
                    }
                }
                else dp[i][j]=dp[i-1][j-1]&&s1[i]==s2[j];
            }
        }

        cout<<(dp[l1-1][l2-1]?"yes":"no")<<endl;
    }
}

/*
 
 --------aa
 --------ab*
 
 */