wust-acm 19级 周练一(A~T)无I

专题地址
因为是acm弱校,所以进度有点拖沓
专题包括(最短路,最小生成树,线段树)(加一个DP,,但DP我没写出来
A~E最短路
F~K最小生成树(不含I
I 动态规划
L~T 线段树(具体分类看后面
------------------------------------------------------

wust-acm 19级 周练一(A~T)无I_第1张图片
一个比较巧妙的题目(先不谈地位差别的限制
我们用金币去买东西,但是可以通过买另外一个人的东西并加一点钱来获得这个东西…仔细想想的话就容易发现这个的本质是一个最短路问题,每个人物品的价格相当于初始的起点到每个点的距离,然后可以交换的物品可以视为两个点之间有路径,需要的价钱就相当于路径的权值,求出需要买到的东西耗费的最小价钱就等效于到这个点的最短路径…
关于地位的限制问题,因为题目数据范围很小只有100,所以我们完全可以暴力枚举地位的区间,如,记录下每个人的地位,然后枚举每个人的地位为最低值,那么我们就只能在val到val+m这个地位区间操作,松弛路径时多一个地位限制就好了

int m,n,tmp;
int ank[maxn],arr[maxn][maxn],val[maxn],dis[maxn],vis[maxn];
bool check(int pos){
    if(ank[pos]>=tmp&&ank[pos]<=tmp+m){
        return true;
    }
    return false;
}
void dijstra(){
    rep(i,1,n){
        dis[i]=val[i];
        vis[i]=0;
    }
    while(1){
        int pos=-1,mn=inf;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                pos=i;
                mn=dis[i];
            }
        }
        if(pos==-1)break;
        vis[pos]=1;
        if(!check(pos))continue;
        rep(i,1,n){					不可松弛地位限制之外的
            if(!vis[i]&&dis[i]>arr[pos][i]+dis[pos]&check(i)){
                dis[i]=arr[pos][i]+dis[pos];
            }
        }
    }
}
void solve(){
    m=read(),n=read();
    rep(i,1,n){
        rep(j,1,n){
            if(i==j)arr[i][j]=0;
            else{
                arr[i][j]=inf;
            }
        }
    }
    rep(i,1,n){
        val[i]=read();
        ank[i]=read();
        int num=read();
        while(num--){
            int t=read(),v=read();
            arr[t][i]=v;
        }
    }
    int ans=inf;
    rep(i,1,n){枚举地位区间
        tmp=ank[i];
        dijstra();
        ans=min(ans,dis[1]);
    }
    printf("%d\n",ans);
}


wust-acm 19级 周练一(A~T)无I_第2张图片
嗯…题意很简单,给出了牛的数目和比赛的场次,然后如果A>B B>C我们就可以得到A>C 如果A 乍一看很懵…其实很简单,我们可以直接暴力floyd求出每两头牛之间的关系,只要这两头牛和另外的一头牛关系可以推出来,如上
(其实有点像离散数学的传递闭包。。
之后遍历每头牛,看它和其他所有的牛关系是否都确定,是的话,这头牛的排名也就确定了
具体方法,我们可以建立一个关系矩阵,初始化每两个点之间的关系为0,代表不确定,然后在输入比赛结果的时候,如果A>B我们就储存AB为1 BA为-1
1代表大于,-1代表小于,然后暴力flyod,如果AB BC关系一致我们就可以得到AC关系了
最后遍历每头牛,看它是不是和其他牛关系都确定(即全都不等于0
计数就好了

int e[maxn][maxn];
void solve(){
    ms(e,0);
    int n=read(),m=read();
    while(m--){
        int a=read(),b=read();
        e[a][b]=1;
        e[b][a]=-1;
    }
    rep(k,1,n){
        rep(i,1,n){
            rep(j,1,n){
                if(e[i][k]*e[k][j]>0){
                    e[i][j]=e[i][k];
                }
            }
        }
    }
    int ans=0;
    rep(i,1,n){
        int f=1;
        rep(j,1,n){
            if(i==j)continue;
            if(!e[i][j]){
                f=0;
                break;
            }
        }
        ans+=f;
    }
    printf("%d\n",ans);
}



这个…大意就是一头牛需要从n点回到1点,给出了所有路径以及权值(无向图
求问最短路径为多少
呃。。。没啥好说的了,dijstra模板题
范围i小,不用写优化的方法就OK(懒
青蛙要回家,路上需要跳石头,求问它最起码能够拥有跳多远的能力才可以回家

int e[maxn][maxn],dis[maxn],vis[maxn];
void dijstra(int st,int n){
    rep(i,1,n){
        vis[i]=0;
        dis[i]=e[st][i];
    }
    vis[st]=1;
    while(1){
        int pos=-1,mn=inf;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                mn=dis[i];
                pos=i;
            }
        }
        if(pos==-1)
        {
            break;
        }
        vis[pos]=1;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>dis[pos]+e[pos][i]){
                dis[i]=dis[pos]+e[pos][i];
            }
        }
    }
}
void solve(){
    int t=read(),n=read();
    rep(i,1,n){
        e[i][i]=0;
        rep(j,i+1,n){
            e[i][j]=e[j][i]=inf;
        }
    }
    while(t--){
        int a=read(),b=read(),c=read();
        e[a][b]=e[b][a]=min(e[a][b],c);
    }
    dijstra(n,n);
    printf("%d\n",dis[1]);
}



青蛙要跳回家,求问它最起码能跳多远才能到家

即求出所有回家路线中,路径中权值最大的最小为多少
这其实是一个dijstra的变式
dis数组含义改成从起点到每个点中权值最大的边最小为多少
和dijstra写的一样,区别在于松弛的方式
原本是找出目前最近的点,然后松弛使得其他点尽可能小,如

 rep(i,1,n){
            if(!vis[i]&&dis[i]>dis[pos]+e[pos][i]){
                dis[i]=dis[pos]+e[pos][i];
            }
        }

我们将其改成,每次找出最近的点,然后松弛其他点使得到其他点的最大权值尽可能小

 rep(i,1,n){	找出其他点是否可以通过这个点使得最大权值更小
            if(!vis[i]&&dis[i]>max(dis[pos],e[pos][i])){
                dis[i]=max(dis[pos],e[pos][i]);
            }
        }

其他的就是常规套路了

int n;
double xx[maxn],yy[maxn];
double e[maxn][maxn],dis[maxn];
int vis[maxn];
double get(int a,int b){
    return sqrt(pow(xx[a]-xx[b],2)+pow(yy[a]-yy[b],2));
}
void dijstra(int st,int n){
    rep(i,1,n){
        vis[i]=0;
        dis[i]=e[st][i];
    }
    vis[st]=1;
    while(1){
        int pos=-1;
        double mn=inf*1.0;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                mn=dis[i];
                pos=i;
            }
        }
        if(pos==-1){break;}
        vis[pos]=1;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>max(dis[pos],e[pos][i])){
                dis[i]=max(dis[pos],e[pos][i]);
            }
        }
    }
}
void solve(){
    int cas=0;
    while(~scanf("%d",&n)&&n){
        rep(i,1,n){
            scanf("%lf %lf",&xx[i],&yy[i]);
        }
        rep(i,1,n){
            rep(j,1,n){
                if(i==j)e[i][j]=0;
                else{
                    e[i][j]=get(i,j);
                }
            }
        }
        dijstra(1,n);
        printf("Scenario #%d\nFrog Distance = %.3f\n\n",++cas,dis[2]);
    }
}


wust-acm 19级 周练一(A~T)无I_第3张图片
简明题意:一辆货车运送货物去目的地,路上很多桥,不能超过桥的承重,求出一次运送最多可以运送多少的货物
很明显你要是能到达目的地的话,那么你就不可以超过去目的地的这条路上所有桥的最小承重
这样一来就和上面那题恰恰相反了,变成了求一条路上最小值的最大为多少
因为是求最大,所以我们初始化dis为0,然后松弛条件是是否可以通过这条边使得这条路上的最小值变得更大,因为和上题几乎差不多,就直接贴代码吧

int n,cas=0;
int dis[maxn],vis[maxn],e[maxn][maxn];
void dijstra(int st,int n){
    rep(i,1,n){
        dis[i]=e[st][i];
    }
    vis[st]=1;
    while(1){
        int pos=-1,mn=0;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>mn){
                mn=dis[i];
                pos=i;
            }
        }
///de(pos);
        if(pos==-1)break;
        vis[pos]=1;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<min(dis[pos],e[pos][i])){
                dis[i]=min(dis[pos],e[pos][i]);
            }
        }
    }
}
void solve(){
    int n=read(),m=read();
    ms(vis,0),ms(e,0);
    while(m--){
        int a=read(),b=read(),c=read();
        e[a][b]=e[b][a]=max(e[a][b],c);
    }
    dijstra(1,n);
    printf("Scenario #%d:\n%d\n\n",++cas,dis[n]);
}


wust-acm 19级 周练一(A~T)无I_第4张图片
最小生成树之prime算法的模板题
给出了n个点之间的关系矩阵,求出一种连线使得整体串联起来且总权值最小
明摆的最小生成树模板,这种给出邻接矩阵的题适合用prime算法来写

int n,e[maxn][maxn],dis[maxn],vis[maxn];
int prime(int st,int n){
    rep(i,1,n){
        vis[i]=0;
        dis[i]=e[st][i];
    }
    dis[st]=0,vis[st]=1;
    int ans=0;
    while(1){
        int pos=-1,mn=inf;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                pos=i;
                mn=dis[i];
            }
        }
        if(pos==-1)break;
        vis[pos]=1;
        ans+=mn;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>e[pos][i]){
                dis[i]=e[pos][i];
            }
        }
    }
    return ans;
}
void solve(){
    while(~scanf("%d",&n)){
    rep(i,1,n){
        rep(j,1,n){
            e[i][j]=read();
        }
    }
    cout<<prime(1,n)<<endl;
    }
}



最小生成树+BFS寻找距离
题意大概就是每次走到一个目标点就可以分成两部分,然后分别走向其他的目的地,求出最后连接所有目的地的路径之和最小是多少,明显可以发现这是一个最小生成树的题目,问题在于两点之间距离怎么求,因为这是一个迷宫,所以距离不等于笛卡尔距离,因为数据范围不大,所以我们可以对每一个点进行bfs来求得每两个点之间的距离为多少
(先记录下每个目的地的坐标并且给他们标号
然后用标号与标号来代替每两个点之间的距离再跑一个最小生成树就好了
(这题我数组开小了…wa了好几遍

int n,m,tot;
int cup[maxn][maxn],dis[maxn],e[maxn][maxn],vis[maxn],used[maxn][maxn];
char str[maxn][maxn];
int nx[4]={0,1,0,-1};
int ny[4]={1,0,-1,0};
struct node{
    int x,y,s;
};
void bfs(int sx,int sy){
    //ms(used,0);
    queue<node>que;
    ms(used,0);
    node now={sx,sy,0};
    que.push(now);
    used[sx][sy]=1;
    while(!que.empty()){
         now=que.front();que.pop();
        rep(i,0,3){
            int tx=now.x+nx[i];
            int ty=now.y+ny[i];
            int s=now.s+1;
            if(tx<1||tx>n||ty<1||ty>m||used[tx][ty]||str[tx][ty]=='#'){
                continue;
            }
            used[tx][ty]=1;
            node nxt={tx,ty,s};
            que.push(nxt);
            if(cup[tx][ty]){
                int a=cup[sx][sy];
                int b=cup[tx][ty];
                e[a][b]=s;
            }
        }
    }
}
int prime(){
    rep(i,1,tot){
        vis[i]=0;
        e[i][i]=0;
        dis[i]=e[1][i];
    }
    vis[1]=1,dis[1]=0;
    int ans=0;
    while(1){
        int pos=-1,mn=inf;
        rep(i,1,tot){
            if(!vis[i]&&dis[i]<mn){
                mn=dis[i];
                pos=i;
            }
        }
        if(pos==-1)break;
        vis[pos]=1;
        ans+=mn;
        rep(i,1,tot){
            if(!vis[i]&&dis[i]>e[pos][i]){
                dis[i]=e[pos][i];
            }
        }
    }
    return ans;
}
void solve(){
    ms(cup,0);
    scanf("%d %d",&m,&n);
    gets(str[0]);
    tot=0;
    rep(i,1,n){
        gets(str[i]+1);
        rep(j,1,m){
            if(str[i][j]=='A'||str[i][j]=='S'){
                cup[i][j]=++tot;
            }
        }
    }
    rep(i,1,n){
        rep(j,1,m){
            if(cup[i][j]){
                bfs(i,j);
            }
        }
    }
    //cout<
    printf("%d\n",prime());
}



题意晦涩…大意是每个字符串代表一个点,然后长度都为7,每两个字符串之间的距离等效于这俩字符串对应位置不同字符的个数…理解了题意这题就很好写了,先处理字符串来求出距离关系矩阵,再跑一个prime就OK了

char str[maxn][maxn];
int e[maxn][maxn],dis[maxn],vis[maxn];
int get(int i,int j){
    int sum=0;
    rep(k,1,7){
        if(str[i][k]!=str[j][k]){
            sum++;
        }
    }
    return sum;
}
int prime(int st,int n){
    rep(i,1,n){
        dis[i]=e[st][i];
        vis[i]=0;
    }
    vis[st]=1,dis[st]=0;
    int ans=0;
    while(1){
        int pos=-1,mn=inf;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                mn=dis[i];
                pos=i;
            }
        }
        if(pos==-1)break;
        vis[pos]=1;
        ans+=mn;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>e[pos][i]){
                dis[i]=e[pos][i];
            }
        }
    }
    return ans;
}
void solve(){
    int n;while(~scanf("%d",&n)&&n){
        getchar();
        rep(i,1,n){
            scanf("%s",str[i]+1);
        }
        rep(i,1,n){
            rep(j,1,n){
                e[i][j]=get(i,j);
            }
        }
        printf("The highest possible quality is 1/%d.\n",prime(1,n));
    }
}

I题先鸽了,暂时不会,wa到自闭



给出了每一个星球的三维坐标以及半径
求出连接所有星球所需的最小权值
最小生成树就不用说了…这题就是需要你处理下输入,把它们改变为星球与星球之间的距离,明显根据坐标求出任意两点之间的距离,然后减去两者的半径,取max(0,dis)就好了

struct node{
    double x,y,z,r;
}arr[maxn];
double e[maxn][maxn],dis[maxn];
int vis[maxn];
double get(int a,int b){
    double dis=sqrt(pow(arr[a].x-arr[b].x,2)+pow(arr[a].y-arr[b].y,2)+pow(arr[a].z-arr[b].z,2));
    return max((double)0,dis-arr[a].r-arr[b].r);
}
void prime(int st,int n){
    rep(i,1,n){
        vis[i]=0;
        dis[i]=e[st][i];
    }
    dis[st]=0,vis[st]=1;
    double ans=0;
    while(1){
        int pos=-1;
        double mn=inf*1.0;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                mn=dis[i];
                pos=i;
            }
        }
        if(pos==-1)break;
        vis[pos]=1;
        ans+=mn;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>e[pos][i]){
                dis[i]=e[pos][i];
            }
        }
    }
    printf("%.3f\n",ans);
}
void solve(){
    int n;while(~scanf("%d",&n)&&n){
        rep(i,1,n){
            scanf("%lf %lf %lf %lf",&arr[i].x,&arr[i].y,&arr[i].z,&arr[i].r);
        }
        rep(i,1,n){
            e[i][i]=0;
            rep(j,i+1,n){
                e[i][j]=e[j][i]=get(i,j);
            }
        }
        prime(1,n);
    }
}


wust-acm 19级 周练一(A~T)无I_第5张图片
这题也是修路。。(最小生成树
不过变化的地方是,有些路已经修好了,让你求出剩下需要修多长的路
我们只需要处理下输入就好了,输入关系矩阵之后,再把已经修好的路的两点之间权值修改成0就可以了

int e[maxn][maxn],dis[maxn],vis[maxn];
int prime(int st,int n){
    rep(i,1,n){
        vis[i]=0;
        dis[i]=e[st][i];
    }
    dis[st]=0,vis[st]=1;
    int ans=0;
    while(1){
        int pos=-1,mn=inf;
        rep(i,1,n){
            if(!vis[i]&&dis[i]<mn){
                mn=dis[i];
                pos=i;
            }
        }
        if(pos==-1)break;
        vis[pos]=1;
        ans+=mn;
        rep(i,1,n){
            if(!vis[i]&&dis[i]>e[pos][i]){
                dis[i]=e[pos][i];
            }
        }
    }
    return ans;
}
void solve(){
    int n=read();
    rep(i,1,n){
        rep(j,1,n){
            e[i][j]=read();
        }
    }
    int q=read();while(q--){
        int a=read(),b=read();
        e[a][b]=e[b][a]=0;
    }
    printf("%d\n",prime(1,n));
}



线段树入门题
1)建树 build build biubiubiu~
2)单点修改
3)区间查询
emmmm…就这样,最简单的线段树操作,算是模板题吧,找个时间整理一下自己的模板

struct node{
    int val;
    int left,right;
}tree[maxn<<2];
void build(int rt,int l,int r){
    tree[rt].left=l;
    tree[rt].right=r;
    if(l==r){
        cin>>tree[rt].val;
    }else{
        build(ls);
        build(rs);
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
    }
}
int cas;
void updata(int rt,int l,int r,int pos,int num){
    if(l==r){
        tree[rt].val+=num;
    }else{
        if(pos<=md){
            updata(ls,pos,num);
        }else{
            updata(rs,pos,num);
        }
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
    }
}
int query(int rt,int l,int r,int a,int b){
    if(l==a&&r==b){
        return tree[rt].val;
    }else{
        if(b<=md){
            return query(ls,a,b);
        }else if(a>md){
            return query(rs,a,b);
        }else{
            return query(ls,a,md)+query(rs,md+1,b);
        }
    }
}
void solve(){
    int n;cin>>n;
    build(1,1,n);
    string ope;cin>>ope;
    cout<<"Case "<<++cas<<":"<<endl;
    while(ope[0]!='E'){
        int a,b;cin>>a>>b;
        if(ope[0]=='A'){
            updata(1,1,n,a,b);
        }else if(ope[0]=='S'){
            updata(1,1,n,a,-1*b);
        }else if(ope[0]=='Q'){
            //sum=0;
            int sum=query(1,1,n,a,b);
            cout<<sum<<endl;
        }
        cin>>ope;
    }
}


wust-acm 19级 周练一(A~T)无I_第6张图片
也是一道线段树模板
需要单点修改,区间查询
只不过这里维护的是区间最大值,记得push_up

struct node{
    int val;
    int l,r;
}tree[maxn<<2];
void build(int rt,int l,int r){
    if(l==r){
        scanf("%d",&tree[rt].val);
    }else{
        build(ls);
        build(rs);
        tree[rt].val=max(tree[rt<<1].val,tree[rt<<1|1].val);
    }
}
void updata(int rt,int l,int r,int pos,int num){
    if(l==r&&r==pos){
        tree[rt].val=num;
    }else{
        if(pos<=md){
            updata(ls,pos,num);
        }else{
            updata(rs,pos,num);
        }
        tree[rt].val=max(tree[rt<<1].val,tree[rt<<1|1].val);
    }
}
int query(int rt,int l,int r,int a,int b){
    if(l==a&&r==b){
        return tree[rt].val;
    }else{
        if(b<=md){
            return query(ls,a,b);
        }else if(a>=md+1){
            return query(rs,a,b);
        }else{
            return max(query(ls,a,md),query(rs,md+1,b));
        }
    }
}
void solve(){
    int n,m;while(~scanf("%d %d",&n,&m)){
    build(1,1,n);
    while(m--){
        getchar();
        char ope;
        int a,b;
        scanf("%c %d %d",&ope,&a,&b);
        //de(ope),de(a),de(b);
        if(ope=='Q'){
            int ans=query(1,1,n,a,b);
            printf("%d\n",ans);
        }else{
            updata(1,1,n,a,b);
        }
    }
    }
}

wust-acm 19级 周练一(A~T)无I_第7张图片

嗯。。扫描线模板题吧
刚开始的时候扫描线恶心了我好久。。一直没理解,后来静下心去看,发现扫描线本质上就是一个区间查询,区间更新 ?
只需要维护线段树内目前存在多长的线段就好了,然后根据线段乘于宽度
可以看我的另一篇博客,是个模板加别的地方的题解点这里
这道题修改的地方大概就是需要维护两个区间长度,一个是覆盖一次的,一个是覆盖超过一次的。。本质上是一样的
然后就是。。这道题说的是左上角和右下角,结果数据给的左下角和右上角。。不知道是题面写错了还是因为原点在左上角。。。我吐了

double X[maxn<<1];
struct node{
    int l,r;
    double len1,len2;
    int be;
}tree[maxn<<2];
struct LINE{
    double l,r,h;
    int mark;
    bool operator < (const LINE &b)const{
        return h<b.h;
    }
}line[maxn<<1];
inline void push_up(int rt){
    int l=tree[rt].l,r=tree[rt].r;
    if(tree[rt].be>1){
        tree[rt].len1=tree[rt].len2=X[r+1]-X[l];
    }else if(tree[rt].be==1){
        tree[rt].len1=X[r+1]-X[l];
        if(l==r){
            tree[rt].len2=0;
        }else{
            tree[rt].len2=tree[ls].len1+tree[rs].len1;
        }
    }else{
        if(l==r){
            tree[rt].len1=tree[rt].len2=0;
        }else{
            tree[rt].len1=tree[ls].len1+tree[rs].len1;
            tree[rt].len2=tree[ls].len2+tree[rs].len2;
        }
    }
}
void build(int rt,int l,int r){
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].len1=tree[rt].len2=0.0;
    tree[rt].be=0;
    if(l==r){
        return;
    }
    build(lson);
    build(rson);
}
void updata(int rt,double L,double R,int mark){
    int l=tree[rt].l,r=tree[rt].r;
    if(X[r+1]<=L||X[l]>=R){
        return ;
    }
    if(L<=X[l]&&R>=X[r+1]){
        tree[rt].be+=mark;
        push_up(rt);
        return ;
    }
    updata(ls,L,R,mark);
    updata(rs,L,R,mark);
    push_up(rt);
}
void solve(){
    int n;scanf("%d",&n);
    rep(i,1,n){
        double xx1,xx2,yy1,yy2;
        scanf("%lf %lf %lf %lf",&xx1,&yy1,&xx2,&yy2);
        line[i*2-1]={xx1,xx2,yy1,1};
        line[i*2]={xx1,xx2,yy2,-1};
        X[i*2-1]=xx1,X[i*2]=xx2;
    }
    n<<=1;
    sort(X+1,X+n+1);
    sort(line+1,line+n+1);
    int tot=unique(X+1,X+n+1)-X-1;
    build(1,1,tot-1);
    double ans=0.0;
    rep(i,1,n-1){
        updata(1,line[i].l,line[i].r,line[i].mark);
        ans+=tree[1].len2*(line[i+1].h-line[i].h);
        //de(tree[1].len2);
    }
    printf("%.2f\n",ans);
}




区间合并的题目
需要维护两个区间,每次优先考虑DS的区间,再考虑NS的区间
注意更新的时候需要两个区间一起更新
这个还不太熟。。打算开个线段树专题刷一刷

struct node{
    int l,r;
    int mx;
    int lx,rx;
    int tag;
}tree[maxn<<2][2];
int cas;
void build(int rt,int l,int r){
    for(int i=0;i<=1;i++){
        tree[rt][i].lx=tree[rt][i].rx=tree[rt][i].mx=r-l+1;
        tree[rt][i].tag=-1;
        tree[rt][i].l=l;
        tree[rt][i].r=r;
    }
    if(l==r){
        return ;
    }
    build(ls);
    build(rs);
}
inline void push_down(int rt,int l,int r){
    //de(rt),de(l),de(r);
    for(int i=0;i<=1;i++){
        if(tree[rt][i].tag!=-1){
            tree[rt<<1][i].mx=tree[rt<<1][i].lx=tree[rt<<1][i].rx=tree[rt][i].tag*(tree[rt<<1][i].r-tree[rt<<1][i].l+1);
            tree[rt<<1|1][i].mx=tree[rt<<1|1][i].lx=tree[rt<<1|1][i].rx=tree[rt][i].tag*(tree[rt<<1|1][i].r-tree[rt<<1|1][i].l+1);
            tree[rt<<1][i].tag=tree[rt<<1|1][i].tag=tree[rt][i].tag;
            tree[rt][i].tag=-1;
        }
    }
}
inline void push_up(int rt,int l,int r){
    for(int i=0;i<=1;i++){
        tree[rt][i].lx=tree[rt<<1][i].lx;
        tree[rt][i].rx=tree[rt<<1|1][i].rx;
        tree[rt][i].mx=max(max(tree[rt<<1][i].mx,tree[rt<<1|1][i].mx),tree[rt<<1][i].rx+tree[rt<<1|1][i].lx);
        if(tree[rt][i].lx==tree[rt<<1][i].r-tree[rt<<1][i].l+1){
            tree[rt][i].lx+=tree[rt<<1|1][i].lx;
        }
        if(tree[rt][i].rx==tree[rt<<1|1][i].r-tree[rt<<1|1][i].l+1){
            tree[rt][i].rx+=tree[rt<<1][i].rx;
        }
    }
}
int query(int rt,int l,int r,int len,int pos){
    if(l==r){
        return l;
    }
        push_down(rt,l,r);

    if(tree[rt<<1][pos].mx>=len){
        return query(ls,len,pos);
    }else if(tree[rt<<1][pos].rx+tree[rt<<1|1][pos].lx>=len){
        return tree[rt<<1][pos].r-tree[rt<<1][pos].rx+1;
    }else{
        return query(rs,len,pos);
    }
}
void updata(int rt,int l,int r,int st,int ed,int tag,int pos){
    if(l==st&&r==ed){
        tree[rt][pos].tag=tag;
        tree[rt][pos].lx=tree[rt][pos].rx=tree[rt][pos].mx=(r-l+1)*tag;
        return ;
    }

        push_down(rt,l,r);
    if(ed<=md){
        updata(ls,st,ed,tag,pos);
    }else if(st>=md+1){
        updata(rs,st,ed,tag,pos);
    }else{
        updata(ls,st,md,tag,pos);
        updata(rs,md+1,ed,tag,pos);
    }
    push_up(rt,l,r);
}
void solve(){
    int t,n;scanf("%d %d",&n,&t);
    build(1,1,n);
    printf("Case %d:\n",++cas);
    while(t--){
        char ope[50];scanf("%s",ope);
        if(ope[0]=='D'){
            int len;scanf("%d",&len);
            if(len<=tree[1][0].mx){
                int pos=query(1,1,n,len,0);
                printf("%d,let's fly\n",pos);
                updata(1,1,n,pos,pos+len-1,0,0);
            }else{
                puts("fly with yourself");
            }
        }else if(ope[0]=='N'){
            int len;scanf("%d",&len);
            int pos;
            if(len>tree[1][0].mx&&len>tree[1][1].mx){
                puts("wait for me");
                continue;
            }else if(tree[1][0].mx>=len){
                pos=query(1,1,n,len,0);
                printf("%d,don't put my gezi\n",pos);
            }else{
                pos=query(1,1,n,len,1);
                printf("%d,don't put my gezi\n",pos);
            }
            updata(1,1,n,pos,pos+len-1,0,0);
            updata(1,1,n,pos,pos+len-1,0,1);
        }else{
            int st,ed;scanf("%d %d",&st,&ed);
            updata(1,1,n,st,ed,1,0);
            updata(1,1,n,st,ed,1,1);
            puts("I am the hope of chinese chengxuyuan!!");
        }
    }
}


wust-acm 19级 周练一(A~T)无I_第8张图片
区间查询,区间更新,二分查找
对于每一次插花的请求,我们就直接查找这个位置到终点共有多少空的位置,然后如果为0,就输出不可能,不为0就取min(num1,num2) 然后就两次二分这个位置和终点 分别求出距离这个位置,总和为1,总和为num1的两个点的下标,然后把这两个点之间修改为插满了花就好了
对于清理花瓶,我们只需要先查询这个区间有多少花,再把这个区间更新为0就可以了

struct node{
    int be;
    int val;
}tree[maxn<<2];
void push_down(int rt,int l,int r){
    if(tree[rt].be!=-1){
        tree[rt<<1].val=tree[rt].be*((r-l+1)-(r-l+1)/2);
        tree[rt<<1|1].val=tree[rt].be*((r-l+1)/2);
        tree[rt<<1].be=tree[rt<<1|1].be=tree[rt].be;
        tree[rt].be=-1;
    }
}
void build(int rt,int l,int r){
    tree[rt].be=-1;
    tree[rt].val=0;
    if(l==r){
        return ;
    }else{
        build(ls);
        build(rs);
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
    }
}
void updata(int rt,int l,int r,int a,int b,int c){
    if(l==a&&r==b){
        tree[rt].val=(b-a+1)*c;
        tree[rt].be=c;
        return ;
    }
    push_down(rt,l,r);
    if(b<=md){
        updata(ls,a,b,c);
    }else if(a>=md+1){
        updata(rs,a,b,c);
    }else{
        updata(ls,a,md,c);
        updata(rs,md+1,b,c);
    }
    tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
}
int query(int rt,int l,int r,int a,int b){
    if(l==a&&r==b){
        return tree[rt].val;
    }else{
        push_down(rt,l,r);
        if(b<=md){
            return query(ls,a,b);
        }else if(a>=md+1){
            return query(rs,a,b);
        }else{
            return query(ls,a,md)+query(rs,md+1,b);
        }
    }
}
void solve(){
    int n=read(),m=read();
    build(1,1,n);
    while(m--){
        int ope=read(),a=read(),b=read();
        if(ope==1){
            a++;
            int num=(n-a+1)-query(1,1,n,a,n);
            //de(num);
            if(num==0){
                puts("Can not put any one.");
            }else{
                num=min(num,b);
                int L=a,R=n,st=-1,ed=-1;

                while(L<=R){
                    int MD=L+R>>1;
                    int tmp=(MD-a+1)-query(1,1,n,a,MD);
                    if(tmp>=1){
                        R=MD-1;
                        st=MD;
                    }else{
                        L=MD+1;
                    }
                }
                L=a,R=n;
                while(L<=R){
                    int MD=L+R>>1;
                    int tmp=(MD-a+1)-query(1,1,n,a,MD);
                    if(tmp>=num){
                        R=MD-1;
                        ed=MD;
                    }else{
                        L=MD+1;
                    }
                }

                printf("%d %d\n",st-1,ed-1);
                updata(1,1,n,st,ed,1);
            }
        }else{
            a++,b++;
            printf("%d\n",query(1,1,n,a,b));
            updata(1,1,n,a,b,0);
        }
    }
    puts("");
}

wust-acm 19级 周练一(A~T)无I_第9张图片
wust-acm 19级 周练一(A~T)无I_第10张图片
因为是求区间的值开方之后的结果…很明显不可以用lazy…很明显不可以。。
所以我们只能暴力开方,但是又会超时…
但我们可以发现,根据数据范围 最多开6,7次方就到1了(向下取整
所以我们就是暴力开方,然后用一个flag标记每一个区间代表这个区间内是否全都为1,是的话就不需要继续开方了…
然后就OK了

int cas;
struct node{
    ll val;
    ll is1;
}tree[maxn<<2];
void build(int rt,int l,int r){
    if(l==r){
        scanf("%lld",&tree[rt].val);
        tree[rt].is1= tree[rt].val==1 ;
    }else{
        build(ls);
        build(rs);
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
        tree[rt].is1= tree[rt<<1].is1&&tree[rt<<1|1].is1;
    }
}
void updata(int rt,int l,int r,int a,int b){
    //de(a),de(b);
    if(tree[rt].is1){
        return ;
    }
    if(l==r){
        tree[rt].val=sqrt(tree[rt].val);
        tree[rt].is1= tree[rt].val==1;
    }else{
        if(b<=md){
             updata(ls,a,b);
        }else if(a>=md+1){
             updata(rs,a,b);
        }else{
             updata(ls,a,md);
             updata(rs,md+1,b);
        }
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
        tree[rt].is1= tree[rt<<1].is1&&tree[rt<<1|1].is1;
    }
}
ll query(int rt,int l,int r,int a,int b){
    //de(a),de(b);
    if(l==a&&r==b){
        return tree[rt].val;
    }else{
        if(b<=md){
            return query(ls,a,b);
        }else if(a>=md+1){
            return query(rs,a,b);
        }else{
            return query(ls,a,md)+query(rs,md+1,b);
        }
    }
}
void solve(){
    int n;while(~scanf("%d",&n)){
        ms(tree,0);
        printf("Case #%d:\n",++cas);
        build(1,1,n);
        int m;scanf("%d",&m);while(m--){
            int ope,a,b;
            scanf("%d %d %d",&ope,&a,&b);
            if(a>b){
                swap(a,b);
            }
            if(ope){
                printf("%lld\n",query(1,1,n,a,b));
            }else{
                updata(1,1,n,a,b);
            }
        }
        puts("");
    }
}

wust-acm 19级 周练一(A~T)无I_第11张图片
wust-acm 19级 周练一(A~T)无I_第12张图片
没啥别的,就是个lazy,只不过是多重lazy…把我整吐了…
记得优先处理乘法,然后记两个标记add,mul初始化为0,1
不等于则说明需要继续向下push_down…我wa惨了…

const int mm=10007;
struct node{
    int l,r,v;
    int add,mul;
}tree[maxn<<2];
void build(int rt,int l,int r){
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].v=-1;
    tree[rt].add=0;
    tree[rt].mul=1;
    if(l==r){
        tree[rt].v=0;
        return ;
    }
    build(lson);
    build(rson);
}
inline void push_down(int rt){
    int l=tree[rt].l,r=tree[rt].r;
    if(l==r){
        return ;
    }
    if(tree[rt].v!=-1){
        tree[ls].v=tree[rs].v=tree[rt].v;
        tree[ls].add=tree[rs].add=0;
        tree[ls].mul=tree[rs].mul=1;
        tree[rt].v=-1;
        return ;
    }
    if(tree[rt].mul!=1){
        if(tree[ls].v!=-1){
            tree[ls].v=(tree[ls].v*tree[rt].mul)%mm;
        }else{
            push_down(ls);
            tree[ls].mul=(tree[ls].mul*tree[rt].mul)%mm;
        }
        if(tree[rs].v!=-1){
            tree[rs].v=(tree[rs].v*tree[rt].mul)%mm;
        }else{
            push_down(rs);
            tree[rs].mul=(tree[rs].mul*tree[rt].mul)%mm;
        }
        tree[rt].mul=1;
    }
    if(tree[rt].add!=0){
        if(tree[ls].v!=-1){
            tree[ls].v=(tree[ls].v+tree[rt].add)%mm;
        }else{
            push_down(ls);
            tree[ls].add=(tree[ls].add+tree[rt].add)%mm;
        }
        if(tree[rs].v!=-1){
            tree[rs].v=(tree[rs].v+tree[rt].add)%mm;
        }else{
            push_down(rs);
            tree[rs].add=(tree[rs].add+tree[rt].add)%mm;
        }
        tree[rt].add=0;
    }
}/*
inline void push_up(int rt,int l,int r){
    if(l==r){
        return ;
    }
    if(tree[ls].v!=-1&&tree[rs].v!=-1&&tree[ls].v==tree[rs].v){
        tree[rt].v=tree[ls].v;
        tree[ls].v=tree[rs].v
    }
}*/
void updata(int rt,int a,int b,int c,int ope){
    int l=tree[rt].l,r=tree[rt].r;
    if(l==a&&r==b){
        if(ope==3){
            tree[rt].v=c;
            tree[rt].add=0,tree[rt].mul=1;
            return ;
        }
        if(tree[rt].v!=-1){
            if(ope==1){
                tree[rt].v=(tree[rt].v+c)%mm;
            }else{
                tree[rt].v=(tree[rt].v*c)%mm;
            }
        }else{
            push_down(rt);
            if(ope==1){
                tree[rt].add=(tree[rt].add+c)%mm;
            }else{
                tree[rt].mul=(tree[rt].mul*c)%mm;
            }
        }
        return ;
    }
    push_down(rt);
    if(b<=md){
        updata(ls,a,b,c,ope);
    }else if(a>=md+1){
        updata(rs,a,b,c,ope);
    }else{
        updata(ls,a,md,c,ope);
        updata(rs,md+1,b,c,ope);
    }
    ///push_up(rt,l,r);
}
int query(int rt,int a,int b,int c){
    int l=tree[rt].l,r=tree[rt].r;
    if(l==a&&r==b&&tree[rt].v!=-1){
        int tmp=1;
        while(c--){
            tmp=(tmp*tree[rt].v)%mm;
        }
        return (r-l+1)*tmp;
    }
    push_down(rt);
    if(b<=md){
        return query(ls,a,b,c);
    }else if(a>=md+1){
        return query(rs,a,b,c);
    }else{
        return query(ls,a,md,c)+query(rs,md+1,b,c);
    }
}
void solve(){
    int n,m;while(~scanf("%d %d",&n,&m)&&(n||m)){
        build(1,1,n);
        while(m--){
            int ope,a,b,c;
            scanf("%d %d %d %d",&ope,&a,&b,&c);
            //de(ope),de(a),de(b),de(c);
            if(ope!=4){
                updata(1,a,b,c,ope);
            }else{
                int ans=query(1,a,b,c);
                printf("%d\n",ans%mm);
            }
        }
    }
}

wust-acm 19级 周练一(A~T)无I_第13张图片
wust-acm 19级 周练一(A~T)无I_第14张图片
离散化+染色问题
因为数据太大了…就需要离散化一下来处理,然后记得染色…记录颜色的总数

struct node{
    int color;
}tree[maxn<<3];
struct q{
    int x,y;
}input[maxn];
int vis[maxn<<3],sum;
void push_down(int rt){
    //de(rt);
    if(tree[rt].color!=-1){
        tree[rt<<1].color=tree[rt<<1|1].color=tree[rt].color;
        tree[rt].color=-1;
    }
}
void updata(int rt,int l,int r,int x,int y,int co){
    if(l==x&&r==y){
        tree[rt].color=co;
        return ;
    }
    push_down(rt);
    if(y<=md){
        updata(ls,x,y,co);
    }else if(x>=md+1){
        updata(rs,x,y,co);
    }else{
        updata(ls,x,md,co);
        updata(rs,md+1,y,co);
    }
}
void query(int rt,int l,int r){
    int co=tree[rt].color;
    if(co!=-1){
        if(!vis[co]){
            vis[co]=1;
            sum++;
        }
        //return ;
    }
    push_down(rt);
    if(l==r){
        return ;
    }else{
        query(rt<<1,l,md);
        query(rt<<1|1,md+1,r);
    }
}
void build(int rt,int l,int r){
    tree[rt].color=-1;
    if(l==r){
        return ;
    }else{
        build(ls);
        build(rs);
    }
}
void solve(){
    vector<int>v;
    ms(vis,0);//ms(tree,-1);
    int n=read();rep(i,0,n-1){
        input[i].x=read(),input[i].y=read();
        v.pb(input[i].x);
        v.pb(input[i].y);
    }
    sort(v.begin(),v.end());
    //de(v.size());
    v.erase(unique(v.begin(),v.end()),v.end());
    //de(v.size());
    int sz=v.size();
    rep(i,1,sz-1){
        if(v[i]-v[i-1]>1){
            v.pb(v[i-1]+1);
        }
    }
    sort(v.begin(),v.end());
    sz=v.size();
    //de(sz);
    /*
    rep(i,0,sz-1){
        cout<<' '<
    build(1,1,sz);
    rep(i,0,n-1){
        int x=lower_bound(v.begin(),v.end(),input[i].x)-v.begin();
        int y=lower_bound(v.begin(),v.end(),input[i].y)-v.begin();
        x++;
        y++;
       // de(x),de(y);
        updata(1,1,sz,x,y,i);
    }
    sum=0;
    query(1,1,sz);
    printf("%d\n",sum);
}

wust-acm 19级 周练一(A~T)无I_第15张图片
lazy入门题吧,区间更新,区间查询
如果单点更新的话会超时…就用一个lazy来记录更新情况
我觉得lazy的难点就在于push_down和push_up
简单的好处理,一旦遇到多种lazy且涉及先后顺序的情况就很难搞了。。

struct node{
    ll l,r;
    ll val,add;
}tree[maxn<<2];
ll sum(ll l,ll r,ll f){
    if(f){
        return (r-l+1)-((r-l+1)>>1);
    }else{
        return (r-l+1)>>1;
    }
}
void build(ll rt,ll l,ll r){
    tree[rt].add=0;
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r){
        cin>>tree[rt].val;
    }else{
        build(ls);
        build(rs);
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
    }
}
void push_down(ll rt,ll l,ll r){
    if(tree[rt].add){
        ll tmp=tree[rt].add;
        tree[rt<<1].add+=tmp;
        tree[rt<<1|1].add+=tmp;

        tree[rt<<1].val+=tmp*sum(l,r,1ll);
        tree[rt<<1|1].val+=tmp*sum(l,r,0ll);

        tree[rt].add=0;
    }
}
void updata(ll rt,ll l,ll r,ll a,ll b,ll c){
    if(l==a&&r==b){
        tree[rt].add+=c;
        tree[rt].val+=(b-a+1)*c;
        return ;
    }
    push_down(rt,l,r);
    if(b<=md){
        updata(ls,a,b,c);
    }else if(a>=md+1){
        updata(rs,a,b,c);
    }else{
        updata(ls,a,md,c);
        updata(rs,md+1,b,c);
    }
    tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
}
ll query(ll rt,ll l,ll r,ll a,ll b){
    if(l==a&&r==b){
        return tree[rt].val;
    }
    push_down(rt,l,r);
    if(b<=md){
        return query(ls,a,b);
    }else if(a>=md+1){
        return query(rs,a,b);
    }else{
        return query(ls,a,md)+query(rs,md+1,b);
    }
}
void solve(){
    ll n,q;while(cin>>n>>q){
    build(1,1,n);while(q--){
        ll a,b,c;
        char ope;cin>>ope;
        switch(ope){
            case 'C': {cin>>a>>b>>c;updata(1,1,n,a,b,c);break;}
            case 'Q': {cin>>a>>b;cout<<query(1,1,n,a,b)<<endl;break;}
        }
    }
    }
}

你可能感兴趣的:(笔记)