Black Rock Shooter
原题:SPOJ GSS3
$ 1. $ 题目大意
在人气动漫 $ Black Rock shooter $ 中,当加贺里对麻陶说出了“滚回去“以后,与此同时,在另一个心灵世界里, $ BRS $ 也遭到了敌人的攻击。此时,一共有 $ n $ 个攻击排成一行朝着她飞了过来,每个攻击有一个伤害值。并且每个攻击伤害可能随时变化。$ BRS $ 的攻击可以打掉一段连续的攻击。现在,给出 $ m $ 段攻击,求出 $ BRS $ 最多可以打掉此段里多少的伤害(就是说从给定一段里选择连续一段打 掉)。伤害从 $ 1 $ 到 $ n $ 编号。
$ 2. $ 输入格式
第一行 $ 2 $ 个整数:$ n , m $
第二行 $ n $ 个数:第 $ i $ 个数代表第 $ i $ 个攻击 第 $ 3 $ 到 $ 2+m $ 行:每行三个数 $ k,x,y $ 。
若 $ k=1,x,y $ 代表查询的区间。
若 $ k=2,$ 代表第 $ x $ 个攻击伤害改为了 $ y $ 所有的伤害值绝对值 $ \le 1000 $
$ 3. $ 输出格式
对于每次 $ k=1 $ ,输出一个整数代表最大值
$ 4. $ 样例输入
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3
$ 5. $ 样例输出
2
-1
$ 6. $ 数据范围
对于 $ 20 % $ 的数据:$ n,m \le 100 $
对于 $ 60 % $ 的数据:$ n,m \le 3000 $
对于 $ 100 % $ 的数据:$ n \le 500000,m \le 100000 $
题解
题意:给定长度为 $ N $ 的数串,$ M $ 个询问,查询 $ [a,b] $ 中的最大连续子段和。
- 线段树上维护4个值的信息
-
- 区间和 $ sum $
最大子段和 $ max $
左端开始的最大子段和 $ lmax $
右端开始的最大子段和 $ rmax $
- 区间和 $ sum $
合并过程见代码
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<algorithm>
#define ll long long
using namespace std;
struct btree{
ll l,r,book;
ll mx,sum,lx,ly;
}tr[2000000];
ll n,m,k,x;
ll y;
void buildtree(ll l,ll r,ll now){
tr[now].l=l;tr[now].r=r;
if(l==r){
scanf("%lld",&tr[now].sum);
tr[now].mx=tr[now].lx=tr[now].ly=tr[now].sum;
return;
}
ll mid=(l+r)>>1;
buildtree(l,mid,now<<1);
buildtree(mid+1,r,now<<1|1);
tr[now].sum=tr[now<<1].sum+tr[now<<1|1].sum;
tr[now].mx=max(max(tr[now<<1].mx,tr[now<<1|1].mx),tr[now<<1].ly+tr[now<<1|1].lx);
tr[now].lx=max(tr[now<<1].lx,tr[now<<1].sum+tr[now<<1|1].lx);
tr[now].ly=max(tr[now<<1|1].ly,tr[now<<1|1].sum+tr[now<<1].ly);
}
void updata(ll x,ll now,ll val){
if(tr[now].l==x&&tr[now].r==x){
tr[now].mx=tr[now].sum=tr[now].lx=tr[now].ly=val;
return;
}
ll mid=(tr[now].l+tr[now].r)>>1;
if(x>mid) updata(x,now<<1|1,val);
else updata(x,now<<1,val);
tr[now].sum=tr[now<<1].sum+tr[now<<1|1].sum;
tr[now].mx=max(max(tr[now<<1].mx,tr[now<<1|1].mx),tr[now<<1].ly+tr[now<<1|1].lx);
tr[now].lx=max(tr[now<<1].lx,tr[now<<1].sum+tr[now<<1|1].lx);
tr[now].ly=max(tr[now<<1|1].ly,tr[now<<1|1].sum+tr[now<<1].ly);
}
btree query(ll l,ll r,ll now){
if(tr[now].l==l&&tr[now].r==r){return tr[now];}
ll mid=(tr[now].l+tr[now].r)>>1;
if(l>mid){return query(l,r,now<<1|1);}
else if(r<=mid) {return query(l,r,now<<1);}
else {
btree lo,ro,no;
lo=query(l,mid,now<<1);
ro=query(mid+1,r,now<<1|1);
no.sum=lo.sum+ro.sum;
no.mx=max(max(lo.mx,ro.mx),lo.ly+ro.lx);
no.lx=max(lo.lx,lo.sum+ro.lx);
no.ly=max(ro.ly,ro.sum+lo.ly);
return no;
}
}
int main(){
scanf("%lld %lld",&n,&m);
buildtree(1,n,1);
for(ll i=0;i<m;i++){
scanf("%lld %d %lld",&k,&x,&y);
if(k==1){printf("%lld\n",query(x,y,1).mx);}
else{updata(x,1,y);}
}
return 0;
}
Fy's dota2
$1. $ 题目大意
$ Fy $ 觉得自己玩 $ cf,lol $ 这种高端游戏已经够厉害了,于是他决定去玩 $ dota2. $ 结果 $ fy $ 的鼠标右键坏了,所以他就等到 $ 2250 $ 买了把闪烁匕首,用跳刀前进,准备去送泉水。但 是 $ fy $ 一次最多前进 $ k $ 的距离,泉水离 $ fy $ 现在的距离是 $ n $ 。 $ Fy $ 想知道他到泉水的方案数。
$ 1. $ 输入格式:
第一行 $ 2 $ 个整数:$ k,n $
$ 2. $ 输出格式:
一行 $ 1 $ 个整数:代表答案对 $ 7777777 $ 取膜的结果
$ 3. $ 样例输入:
2 4
$ 4. $ 样例输出
5
$ 5. $ 样例解释
一共有 $ 5 $ 种方案
$ →1→2→3→4 $
$ →2→3→4 $
$ →2→4 $
$ →1→3→4 $
$ →1→2→4 $
$ 6. $ 数据范围
对于 $ 30 % $ 的数据:$ n \le 1000,k \le 10 $
对于 $100 % $ 的数据:$ 1 \le n \le 2^31 -1,1 \le k \le 10 $
题解
递推公式十分明显:
$ F[i] $ 代表走的距离为 $ i $ 时的方案数
$ F[i]= \sum_{j=i-k}^{i-1} F[j] (F[0]=1) $
由于 $ n $ 较大,直接使用矩阵加速优化即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define mod 7777777
int k,n;
ll ans,f[11]={0,1,2,4,8,16,32,64,128,256,512};
struct matrix{ ll a[11][11]; }st,rec;
inline matrix X(matrix x,matrix y){
matrix c;
memset(c.a,0,sizeof(c.a));
for(int i=1;i<=k;++i)
for(int j=1;j<=k;++j)
for(int l=1;l<=k;++l)
c.a[i][j]=(c.a[i][j]+(x.a[i][l]*y.a[l][j])%mod)%mod;
return c;
}
inline matrix qpow(matrix x,int s){
matrix res=x; --s;
while(s){
if(s&1) res=X(res,x);
x=X(x,x);
s>>=1;
}
return res;
}
int main(){
freopen("fyfy.in","r",stdin);
freopen("fyfy.out","w",stdout);
scanf("%d %d",&k,&n);
if(n<=k){ puts("1"); return 0; }
for(int i=1;i<k;++i) st.a[i][i+1]=1;
for(int i=1;i<=k;++i) st.a[k][i]=1;
rec=qpow(st,n-k);
for(int i=1;i<=k;++i) ans=(ans+(f[i]*rec.a[k][i])%mod)%mod;
printf("%d",ans);
return 0;
}
Olddriver’s books
原题:POJ 1151 Atlantis (原题坐标和面积是带小数点的)
$ 1. $ 题目大意:
$ Olddriver $ 的书多的吓人,什么算法导论,组合数学英文版 \((orz)\) 。。。。。。他把 $ n $ 本书都放在身后的桌子上, 每本书有一定的面积,并且书可以覆盖,求 $ n $ 本书覆盖桌面的面积
$ 2. $ 输入格式:
输入第一行为一个数 $ N $ ,表示书的数量。
下面 $ N $ 行,每行 四个整数,分别表示每个书的左下角和右上角的坐标。
$ 3. $ 输出格式:
输出只有一行,一个整数,表示图形的面积。
$ 4. $ 样例输入:
3
1 1 4 3
2 -1 3 2
4 0 5 2
$ 5. $ 样例输出:
10
$ 6. $ 数据范围:
对于 $ 30 % $ 的数据:$ n \le 100 $ ,坐标数值绝对值 $ \le 300 $
对于 $ 100 % $ 的数据:$ n \le 100 $ ,坐标数值绝对值 $ \le10^8 $
题解
裸的线段树扫描线。。。 $ yyh $ 说这道题 n 很小,不用扫描线用神奇做法也可以。。。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 500
#define ll long long
struct tree{ ll l,r,sum; }t[maxn<<3];
struct data{ ll x,y1,y2; int f; }p[maxn<<3];
int n,cases,lazy[maxn<<3];
ll ans,s[maxn<<3];
bool cmp(data a,data b){ return a.x<b.x; }
void pushup(int o){
if(lazy[o]>0) t[o].sum=t[o].r-t[o].l;
else t[o].sum=t[o<<1].sum+t[o<<1|1].sum;
}
void build(int o,int l,int r){
t[o].l=s[l]; t[o].r=s[r];
if(r-l<=1){ t[o].sum=0; return; }
int mid=l+r>>1;
build(o<<1,l,mid); build(o<<1|1,mid,r);
pushup(o);
}
void updata(int o,ll y1,ll y2,int flag){
if(t[o].l==y1&&t[o].r==y2){ lazy[o]+=flag; pushup(o); return; }
if(t[o<<1].r>y1) updata(o<<1,y1,min(t[o<<1].r,y2),flag);
if(t[o<<1|1].l<y2) updata(o<<1|1,max(t[o<<1|1].l,y1),y2,flag);
pushup(o);
}
int main(){
freopen("olddriver.in","r",stdin);
freopen("olddriver.out","w",stdout);
scanf("%d",&n);
ans=0;
ll x1,x2,y1,y2;
for(int i=1;i<=n;++i){
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
p[i].x=x1; p[i].y1=y1; p[i].y2=y2; p[i].f=1;
p[i+n].x=x2; p[i+n].y1=y1; p[i+n].y2=y2; p[i+n].f=-1;
s[i]=y1; s[i+n]=y2;
}
sort(s+1,s+1+2*n);
sort(p+1,p+1+2*n,cmp);
build(1,1,2*n);
memset(lazy,0,sizeof(lazy));
for(int i=1;i<=2*n;++i){
ans+=(p[i].x-p[i-1].x)*t[1].sum;
updata(1,p[i].y1,p[i].y2,p[i].f);
}
printf("%lld",ans);
return 0;
}
- 这是zkq的神奇做法我没拿到yyh的程序,但是zkq的神奇做法也A了,应该是一样的
#include<bits/stdc++.h>
using namespace std;
struct books
{
int x1,y1,x2,y2;
}c[510];
bool b[510][510];
int x[510],y[510];
int rx[510],ry[510],tx,ty;
int n,cnt;
long long ans;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d",&c[i].x1,&c[i].y1,&c[i].x2,&c[i].y2);
x[++cnt]=c[i].x1;
y[cnt]=c[i].y1;
x[++cnt]=c[i].x2;
y[cnt]=c[i].y2;
}
sort(x+1,x+cnt+1);
sort(y+1,y+cnt+1);
for(int i=1;i<=cnt;i++)
{
if(i==1||x[i]!=x[i-1])rx[++tx]=x[i];
if(i==1||y[i]!=y[i-1])ry[++ty]=y[i];
}
for(int i=1;i<=n;i++)
{
int X1,X2,Y1,Y2;
int l=1,r=tx;
while(l<r)
{
int mid=(l+r+1)>>1;
if(rx[mid]<=c[i].x1)l=mid;
else r=mid-1;
}
X1=l;
l=1,r=tx;
while(l<r)
{
int mid=(l+r+1)>>1;
if(rx[mid]<=c[i].x2)l=mid;
else r=mid-1;
}
X2=l;
l=1,r=ty;
while(l<r)
{
int mid=(l+r+1)>>1;
if(ry[mid]<=c[i].y1)l=mid;
else r=mid-1;
}
Y1=l;
l=1,r=ty;
while(l<r)
{
int mid=(l+r+1)>>1;
if(ry[mid]<=c[i].y2)l=mid;
else r=mid-1;
}
Y2=l;
for(int j=X1;j<X2;j++)
for(int k=Y1;k<Y2;k++)
b[j][k]=1;
}
for(int i=1;i<tx;i++)
for(int j=1;j<ty;j++)
if(b[i][j])ans+=1ll*(rx[i+1]-rx[i])*(ry[j+1]-ry[j]);
printf("%lld",ans);
return 0;
}