http://acm.buaa.edu.cn/problem/1381/
贞鱼传教
【问题描述】
新的一年到来了,贞鱼哥决定到世界各地传授“贞教”,他想让“贞教”在2016年成为世界第四大宗教。说干就干......
贞鱼哥把即将接受传教的人排成一行,每个人从左到右的编号为1-n。每个人有一个信仰值,一开始所有人的信仰值为0。接着贞鱼哥会做以下两件事之一:
1.贞鱼哥向连续的一段人群传教。具体来说,贞鱼哥会使第l个人到第r个人之间的所有人的信仰值增加k。
2.贞鱼哥想知道某一段人的信仰值之和,于是他想找他的同桌DZY来帮忙,但是DZY要求他放出JY,才肯帮忙,贞鱼哥只好放了JY。具体来说,贞鱼哥会问DZY第l个人到第r个人之间所有人的信仰值之和。
然而之前已经无数次介绍:DZY智商低下,太笨了!于是他想找帮过他多次大忙的计算机大神WJC来帮忙,但是WJC去相亲了,所以他找到了机智的你。
【输入格式】
第一行包含两个整数n,m。分别表示接受传教的人数以及贞鱼哥的操作数。
接下来m行,每行包含若干个整数。若第一个整数为1,则代表贞鱼哥在进行传教,这一行接下来会包含l,r,k三个整数。否则第一个整数为2,代表贞鱼哥的一个询问,这一行接下来会包含l,r两个整数。
【输出格式】
输出若干行,每行包含1个整数,表示贞鱼哥询问的答案。行数为贞鱼哥的询问数量。
【样例输入】
5 5
2 1 2
1 4 4 2
1 2 4 7
1 4 5 6
2 3 4
【样例输出】
0
22
【样例说明】
一开始所有人的信仰值都为0,所以答案为0。到第五个操作时,五个人的信仰值分别为0,7,7,15,6。则答案为7+15=22
【数据范围】
对于30%测试点,n,m<=10,0<l<=n,0<r<=n,0<k<=10;
对于60%测试点,n,m<=10^2,0<l<=n,0<r<=n,0<k<=10;
对于100%测试点,n,m<=10^3,0<l<=n,0<r<=n,0<k<=10;
命题人:Hacker_WJC
一看题目果断想到线段树区间更新,区间查询。写了一发。照着模板调了一会(好弱)过了
/* *********************************************** Author :buaaasd Created Time :2016/1/23 19:48:00 File Name :1.cpp ************************************************ */ #include <iostream> #include <cstring> #include <cstdlib> #include <stdio.h> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <iomanip> #include <list> #include <deque> #include <stack> #define ull unsigned long long #define ll long long #define mod 90001 #define INF 0x3f3f3f3f #define maxn 100000+10 #define cle(a) memset(a,0,sizeof(a)) const ull inf = 1LL << 61; const double eps=1e-5; using namespace std; bool cmp(int a,int b){ return a>b; } struct node{ int l,r; ll sum; ll c; }nod[maxn*4]; void push_up(int i){ nod[i].sum=nod[i<<1].sum+nod[i<<1|1].sum; } void push_down(int i){ if(nod[i].c){ nod[i<<1].c+=nod[i].c; nod[i<<1|1].c+=nod[i].c; nod[i<<1].sum+=(ll)(nod[i<<1].r-nod[i<<1].l+1)*nod[i].c; nod[i<<1|1].sum+=(ll)(nod[i<<1|1].r-nod[i<<1|1].l+1)*nod[i].c; nod[i].c=0; } } void build(int i,int l,int r){ nod[i].l=l; nod[i].r=r; nod[i].c=0; if(l==r){ nod[i].sum=0; return ; } int mid=(l+r)/2; build(i<<1,l,mid); build(i<<1|1,mid+1,r); push_up(i); } void update(int i,int l,int r,ll w){ if(nod[i].l==l&&nod[i].r==r){ nod[i].c+=w; nod[i].sum+=(r-l+1)*w; return ; } if(nod[i].l==nod[i].r)return ; push_down(i); int mid=(nod[i].l+nod[i].r)/2; if(r<=mid)update(i<<1,l,r,w); else if(l>mid)update(i<<1|1,l,r,w); else{ update(i<<1,l,mid,w); update(i<<1|1,mid+1,r,w); } push_up(i); } ll query(int i,int l,int r){ if(nod[i].l==l&&nod[i].r==r){ return nod[i].sum; } push_down(i); int mid=(nod[i].l+nod[i].r)/2; ll ans=0; if(r<=mid)ans+=query(i<<1,l,r); else if(l>mid)ans+=query(i<<1|1,l,r); else { ans+=query(i<<1,l,mid); ans+=query(i<<1|1,mid+1,r); } push_up(i); return ans; } int main() { #ifndef ONLINE_JUDGE //freopen("in.txt","r",stdin); #endif //freopen("out.txt","w",stdout); int n,m; int x,y,z,w; while(cin>>n>>m){ build(1,1,n); for(int i=1;i<=m;i++){ scanf("%d",&x); if(x==1){ scanf("%d%d%d",&y,&z,&w); update(1,y,z,w); } else{ scanf("%d%d",&y,&z); printf("%lld\n",query(1,y,z)); } } } return 0; }
贞鱼传教(数据加强版)http://acm.buaa.edu.cn/problem/1382/
贞鱼传教
【问题背景】
计算机大神 Hacker_WJC 发现原先的题目:贞鱼传教连智商低下的DZY都能做出来,于是加强了数据,23333333333333333333333~
【问题描述】
新的一年到来了,贞鱼哥决定到世界各地传授“贞教”,他想让“贞教”在2016年成为世界第四大宗教。说干就干......
贞鱼哥把即将接受传教的人排成一行,每个人从左到右的编号为1-n。每个人有一个信仰值,一开始所有人的信仰值为0。接着贞鱼哥会做以下两件事之一:
1.贞鱼哥向连续的一段人群传教。具体来说,贞鱼哥会使第l个人到第r个人之间的所有人的信仰值增加k。
2.贞鱼哥想知道某一段人的信仰值之和,于是他想找他的同桌DZY来帮忙,但是DZY要求他放出JY,才肯帮忙,贞鱼哥只好放了JY。具体来说,贞鱼哥会问DZY第l个人到第r个人之间所有人的信仰值之和。
然而之前已经无数次介绍:DZY智商低下,太笨了!于是他想找帮过他多次大忙的计算机大神WJC来帮忙,但是WJC去相亲了,所以他找到了机智的你。
【输入格式】
第一行包含两个整数n,m。分别表示接受传教的人数以及贞鱼哥的操作数。
接下来m行,每行包含若干个整数。若第一个整数为1,则代表贞鱼哥在进行传教,这一行接下来会包含l,r,k三个整数。否则第一个整数为2,代表贞鱼哥的一个询问,这一行接下来会包含l,r两个整数。
【输出格式】
输出若干行,每行包含1个整数,表示贞鱼哥询问的答案。行数为贞鱼哥的询问数量。
【样例输入】
5 5
2 1 2
1 4 4 2
1 2 4 7
1 4 5 6
2 3 4
【样例输出】
0
22
【样例说明】
一开始所有人的信仰值都为0,所以答案为0。到第五个操作时,五个人的信仰值分别为0,7,7,15,6。故答案为7+15=22。
【数据范围】
对于30%测试点,n,m<=10,0<l<=n,0<r<=n,k<=10
对于60%测试点,n,m<=10^3,0<l<=n,0<r<=n,k<=10
对于所有测试点,n<=10^9,m<=10^3,0<l<=n,0<r<=n,k<=10
命题人:Hacker_WJC
这个题目是上一个题目的数据加强,没多想把上面那个线段树代码交了一发 re 调了半天没调出来.
试了试离散化,发现不对。不能离散化。
思维定势啊,卡了很久。后来看题解用的模拟的方法。
/* *********************************************** Author :buaaasd Created Time :2016/1/23 19:48:00 File Name :1.cpp ************************************************ */ #include <iostream> #include <cstring> #include <cstdlib> #include <stdio.h> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <iomanip> #include <list> #include <deque> #include <stack> #define ull unsigned long long #define ll long long #define mod 90001 #define INF 0x3f3f3f3f #define maxn 100000+10 #define cle(a) memset(a,0,sizeof(a)) const ull inf = 1LL << 61; const double eps=1e-5; using namespace std; bool cmp(int a,int b){ return a>b; } int main() { #ifndef ONLINE_JUDGE // freopen("in.txt","r",stdin); #endif //freopen("out.txt","w",stdout); int n,m; ll u,x,y; ll k,l,r; while(cin>>n>>m){ int num=0; ll qx[1110],qy[1110],qz[1110],qk[1110]; for(int i=1;i<=m;i++){ scanf("%lld %lld %lld",&u,&x,&y); if(u==1){ scanf("%lld",&k); num++; qx[num]=x; qy[num]=y; qk[num]=k; } else{ ll ans=0; for(int j=1;j<=num;j++){ l=qx[j]; r=qy[j]; k=qk[j]; if(l<=x&&r>=y){ ans+=(y-x+1)*k; } else if(l>=x&&r<=y){ ans+=(r-l+1)*k; } else if(r>=x&&r<=y&&l<x){ ans+=(r-x+1)*k; } else if(r>y&&l<=y&&l>=x){ ans+=(y-l+1)*k; } } printf("%lld\n",ans); } } } return 0; }
这种姿势挺帅的。