沈阳集训day6

然后发两道暴力

三角形


【问题描述】


给定一个三角形数阵,一共 n 行,第 i 行有 i 个数。 此数阵初始全部为 0.
你需要支持以下两个操作:
1.将一个子三角数阵里的数全部加 1。 2.询问一个子三角数阵里的数的和。 我们将以三元组(x,y,a)来描述一个子三角形数阵。 这个数阵中的第一行的元素是(x,y)。 共有 a 行,第 i 行从(x+i-1,y)到(x+i-1,y+i-1)。 注意:(x,y)表示第 x 行的第 y 个数。

【输入格式】


输入文件为 delta.in。 第一行,两个整数 N 和 Q,代表三角形的规模和操作数。 以下 Q 行,每行四个整数,第一个数为操作的编号、接下来三个数为子三 角形数阵三元组(xi,yi,ai)。

【输出格式】


输出文件为 delta.out。 对于每个 2 类操作,输出一行包含一个整数作为答案。
3 9
2 1 1 2
1 1 1 2
2 1 1 2
1 2 1 2
2 1 1 2
2 2 2 2
1 2 2 2
2 1 1 2
2 1 1 3
【样例输出】
0
3
4
2
5
9

暴力三十分题解,显然我建了一千棵线段树233,不会高级数据结构啊哭泣

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 500000+500
#define MINN 1000+10
#define INF 0x7ffffff
struct Tree{
    int sum,add,lson,rson;
}tree[MAXN*4];
int tail,N,Q;
int root[MINN];
void pushup(int rt){
    tree[rt].sum=tree[tree[rt].lson].sum+tree[tree[rt].rson].sum;
}
void pushdown(int rt,int lsum,int rsum){
    if(tree[rt].add){
        tree[tree[rt].lson].add+=tree[rt].add;
        tree[tree[rt].rson].add+=tree[rt].add;
        tree[tree[rt].lson].sum+=lsum*tree[rt].add;
        tree[tree[rt].rson].sum+=rsum*tree[rt].add;
        tree[rt].add=0;
    }
}
void build(int rt,int l,int r){
    tree[rt].lson=++tail;
    tree[rt].rson=++tail;
    if(l==r){
        tree[rt].sum=tree[rt].add=0;
        return ;
    }
    int m=(l+r)>>1;
    build(tree[rt].lson,l,m);
    build(tree[rt].rson,m+1,r);
    pushup(rt);
}
void modify(int rt,int l,int r,int L,int R,int delta){
    if(L<=l&&R>=r){
        tree[rt].sum+=delta*(r-l+1);
        tree[rt].add+=delta;
        return;
    }
    int m=(l+r)>>1;
    pushdown(rt,m-l+1,r-m);
    if(m>=L) modify(tree[rt].lson,l,m,L,R,delta);
    if(m<R)  modify(tree[rt].rson,m+1,r,L,R,delta);
    pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
    if(L<=l&&R>=r){
        return tree[rt].sum;
    }
    int m=(l+r)>>1;
    pushdown(rt,m-l+1,r-m);
    int ass=0;
    if(m>=L) ass+=query(tree[rt].lson,l,m,L,R);
    if(m<R)  ass+=query(tree[rt].rson,m+1,r,L,R);
    return ass;
}
int main(){
    freopen("delta.in","r",stdin);
    freopen("delta.out","w",stdout);
    scanf("%d%d",&N,&Q);
    for(register int i=1;i<=N;i++){
        tail++;tree[tail].sum=tree[tail].add=0;
    }//先给所有根分配节点 
    for(register int i=1;i<=N;i++){
        build(i,1,i);//按照之前分配的节点,i为该树的根,该树的区间为1到i;
    }
    for(register int i=1;i<=Q;i++){
        int temp,x,y,a;
        scanf("%d",&temp);
        if(temp==1){//modify;
            scanf("%d%d%d",&x,&y,&a);
            for(register int i=x;i<=x+a-1;i++){
                modify(i,1,i,y,y+i-x,1);
            }
        }else{
            scanf("%d%d%d",&x,&y,&a);
            int ANS=0;
            for(register int i=x;i<=x+a-1;i++){
                ANS+=query(i,1,i,y,y+i-x);
            }
            printf("%d\n",ANS);
        }
    }
    return 0;
}

下面给上正解

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int SZ=1000,N=1005;
typedef long long ll;
int tag[N][N],x[N],y[N],l[N];
int i,j,n,qnum,t;
ll sum[N][N];
int read(){
    int x=0;char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x;
}
void rebuild(){
    memset(tag,0,sizeof(tag));
    for (;t;t--){
        int x0=x[t],y0=y[t],l0=l[t];
        int mx=x0+l0;
        for (i=y0,j=x0;i<y0+l0;i++,j++){
            tag[i][j]++;tag[i][mx]--;
        }
    }
    for (i=1;i<=n;i++){
        ll now=0,now2=0;
        for (j=i;j<=n;j++){
            now+=(ll)tag[i][j];
            now2+=now;
            sum[i][j]+=now2;
        }
    }
}
int main(){
    freopen("delta.in","r",stdin);
    freopen("delta.out","w",stdout);
    n=read();qnum=read();
    t=0;
    while (qnum--){
        int opt=read();
        if (opt==1){
            t++;
            x[t]=read();y[t]=read();l[t]=read();
            if (t==SZ) rebuild();
        }else{
            int x0=read(),y0=read(),l0=read();
            int mx=x0+l0-1;
            ll ans=0;
            for (i=y0,j=x0-1;i<y0+l0;i++,j++)
            ans+=sum[i][mx]-sum[i][j];
            for (i=1;i<=t;i++){
               int xx=min(x0+l0-1,x[i]+l[i]-1),yy=max(y0,y[i]),l1=max(x0-y0,x[i]-y[i]);
                if (x0>xx||x[i]>xx) continue;
                if (y0+l0-1<yy||y[i]+l[i]-1<yy) continue;
                if (x0+l0-1-y0<l1||x[i]+l[i]-1-y[i]<l1) continue;
                int len=xx-yy-l1+1;
                ans+=(ll)len*(len+1)/2;
            }
        printf("%lld\n",ans);
        }
    }
    return 0;
}

好不容易在markdown上面调整了一下标程的代码风格,竟然括号要换行?太丑了


大逃亡(escape)


【问题描述】

给出数字 N(1<=N<=10000)、X(1<=X<=1000)、Y(1<=Y<=1000)代表 有 N 个敌人分布在一个 X 行 Y 列的矩阵上,矩形的行号从 0 到 X-1、列号从 0 到 Y-1。再给出四个数字 x1,y1,x2,y2 分别代表你要从起点(x1,y1)移动到目标点 (x2,y2)。在移动的过程中你当然希望离敌人的距离的最小值最大化,现在请求出 这个值最大可以为多少?以及在这个前提下,你最少要走多少步才可以到目标 点。
注意这里距离的定义为两点的曼哈顿距离,即某两个点的坐标分为(a,b),(c,d), 那么它们的距离为|a-c|+|b-d|。

【输入格式】


第一行 3 个整数为 N,X,Y 第二行 4 个整数为 x1,y1,x2,y2 下面将有 N 行,为 N 个敌人所在的坐标。

【输出格式】


在一行内输出你离敌人的距离及在这个距离的限制下,你到目标点最少要移 动多少步。

【样例输入】


2 5 6
0 0 4 0
2 1 2 3

【样例输出】


2 14

暴力思想,每次扩大敌人的影响力(把敌人方圆多少的地方染黑,走的时候判断),每次spfa或者a star 或者直接bfs加vis数组爆搜一次,直到走不通为止
正解的思想比我多了个二分,我只过了40分

暴力:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#define MAXN 1000+10
using namespace std;
int N,X,Y,x1,y1,x2,y2,x,y;
int mov[5][2]={0,0,0,1,0,-1,1,0,-1,0};
queue<int>qx,qy;
bool vis[MAXN][MAXN],G[MAXN][MAXN];
int bfs(){
    vis[x1][y1]=true;
    queue<int>qxx,qyy,qlen;
    qxx.push(x1);qyy.push(y1);qlen.push(0);
    do{
        int xx=qxx.front();qxx.pop();
        int yy=qyy.front();qyy.pop();
        int len=qlen.front();qlen.pop();
        for(int i=1;i<=4;i++){
            int xxx=xx+mov[i][0];
            int yyy=yy+mov[i][1];
            if(xxx==x2&&yyy==y2) return len+1;
            if(xxx>=0&&xxx<X&&yyy>=0&&yyy<Y&&(!vis[xxx][yyy])&&(!G[xxx][yyy])){
                qxx.push(xxx);
                qyy.push(yyy);
                vis[xxx][yyy]=true;
                qlen.push(len+1);
            }
        }
        if(qxx.empty())break;
    }while(true);
    return 0;
}
void build(){
    memset(vis,false,sizeof(vis));
    int size=qx.size();
    for(int i=1;i<=size;i++){
        int xx=qx.front();qx.pop();
        int yy=qy.front();qy.pop();
        for(int i=1;i<=4;i++){
            int xxx=xx+mov[i][0];
            int yyy=yy+mov[i][1];
            if(xxx>=0&&xxx<X&&yyy>=0&&yyy<Y&&(!G[xxx][yyy])){
                G[xxx][yyy]=true;
                qx.push(xxx);
                qy.push(yyy); 
            }
        }
    }
}
int main(){
    freopen("escape.txt","r",stdin);
    //freopen("escape.out","w",stdout);
    scanf("%d%d%d",&N,&X,&Y);
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    for(register int i=1;i<=N;i++){
        scanf("%d%d",&x,&y);
        G[x][y]=true;
        qx.push(x);
        qy.push(y);
    }
    int prelen=0,cnt=0;
    while(true){
        int len=bfs();
        if(len==0) break;
        prelen=len;
        cnt++;
        build();
    }
    printf("%d %d\n",cnt,prelen);
} 

正解:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
bool mark[1010][1010];
int map[1010][1010];
int x[5000010],y[5000010];
int bs[1010][1010];
int n,m,k,tou,wei,qx,qy,zx,zy;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void init()
{
    int i,a,b;
    scanf("%d%d%d",&k,&n,&m);
    tou=1;wei=0;
    scanf("%d%d%d%d",&qx,&qy,&zx,&zy);
    qx++;qy++;zx++;zy++;
    for(i=1;i<=k;i++)
    {
        scanf("%d%d",&a,&b);
        a++;b++;
        wei++;x[wei]=a;y[wei]=b;
        mark[a][b]=1;
    }
}
void bfs()
{
    int i,nx,ny,xx,yy,p;
    while(tou<=wei)
    {
        xx=x[tou];yy=y[tou];tou++;
        for(p=0;p<4;p++)
        {
            nx=xx+dx[p];ny=yy+dy[p];
            if(nx<1 || nx>n || ny<1 || ny>m)   continue;
            if(!mark[nx][ny])
            {
                mark[nx][ny]=1;map[nx][ny]=map[xx][yy]+1;
                wei++;x[wei]=nx;y[wei]=ny;
            }
        }   
    }
}
bool check(int mid)
{
    int i,xx,yy,nx,ny,p;
    tou=1;wei=1;x[1]=qx;y[1]=qy;
    memset(mark,0,sizeof(mark));
    memset(bs,127,sizeof(bs));
    mark[qx][qy]=1;bs[qx][qy]=0;
    while(tou<=wei)
    {
        xx=x[tou];yy=y[tou];tou++;
        for(p=0;p<4;p++)
        {
            nx=xx+dx[p];ny=yy+dy[p];
            if(nx<1 || nx>n || ny<1 || ny>m || map[nx][ny]<mid)   continue;
            if(!mark[nx][ny])
            {
                mark[nx][ny]=1;bs[nx][ny]=bs[xx][yy]+1;
                wei++;x[wei]=nx;y[wei]=ny;
            }
        }
    }
    if(mark[zx][zy])  return 1;
    else return 0;
}
int main()
{
    freopen("escape.in","r",stdin);
    freopen("escape.out","w",stdout);
    init();
    bfs();
    int l=0,r=map[qx][qy],mid;
    while(l+1<r)
    {
        mid=(l+r)>>1;
        if(check(mid))  l=mid;
        else r=mid;
    }
    if(check(r)) 
    {cout<<r<<" "<<bs[zx][zy]<<endl;}
    else
    {check(l);cout<<l<<" "<<bs[zx][zy]<<endl;}
//  system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值