这场前四题三个fb好爽~然后第5题线段树被虐的也好爽。。。。。
A. 游戏 2014新生暑假个人排位赛08
题目描述
小弱发明了一个好玩的游戏,游戏开始时给你一个凸多边形,这个多边形可以任意旋转,平移。让你造出一个正方形“容器”(足够大),容器的两边垂直,两边水平。用这个容器把这个多边形完全包含,且多边形有且只有一条边恰好与容器下边界重合(与外界相通),不同的边与外界相通代表不同的方案。现在让你判断是否有方案可以让这个多边形能够不掉下来。不掉下来要求是至少有一条边与容器之间存在压力(假设摩擦系数无穷大)。
如下图,左边会掉下来,右边不会掉下来。
输入格式
有多组数据。
对于每组数据,第一行是多边形点数n(3 <= n <= 1000),后面n行分别是这些点沿着多边形逆时针的二维坐标(xi, yi)(xi,yi的绝对值 <= 1000,输入数据精度精确到两位)。
输出格式
对每组数据输出有多少种方案可以使多边形不掉下来。
输入样例
3
0.00 0.00
2.00 0.00
1.00 1.00
3
0.00 0.00
1.00 0.00
2.00 1.00
4
0.00 0.00
4.00 0.00
3.00 2.00
1.00 2.00
输出样例
0
2
3
#include <iostream>
#include <cstdio>
#define maxn 1005
using namespace std;
int n,ans;
double x[maxn],y[maxn];
bool isDunjiao(double x0,double y0,double x1,double y1,double x2,double y2)
{
if((x1-x0)*(x2-x0)+(y1-y0)*(y2-y0)<0) return true;
return false;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
ans=0;
for(int i=2;i<=n-2;i++)
if(isDunjiao(x[i],y[i],x[i-1],y[i-1],x[i+1],y[i+1]) || isDunjiao(x[i+1],y[i+1],x[i],y[i],x[i+2],y[i+2]) ) ans++;
if(isDunjiao(x[n-1],y[n-1],x[n-2],y[n-2],x[n],y[n]) || isDunjiao(x[n],y[n],x[n-1],y[n-1],x[1],y[1]) ) ans++;
if(isDunjiao(x[n],y[n],x[n-1],y[n-1],x[1],y[1]) || isDunjiao(x[1],y[1],x[n],y[n],x[2],y[2]) ) ans++;
if(isDunjiao(x[1],y[1],x[n],y[n],x[2],y[2]) || isDunjiao(x[2],y[2],x[1],y[1],x[3],y[3]) ) ans++;
printf("%d\n",ans);
}
return 0;
}
-------------------------------------------------------------------------------------------------------------
B. 小妹妹送快递 2014新生暑假个人排位赛08
题目描述
Mays王国的女王大人每天过着自由自在的生活,她最大的乐趣就是给邻国的帅气王子写信。但是最近,Mays王国的叔叔们变得很无聊,他们知道女王大人每次都把信委托给皇家小妹妹快递公司的小妹妹们,于是叔叔们给每一条路都设立了路障,只有小妹妹们给他们表演节目才会让小妹妹们过去。
在每一个路障,都有不同数量的叔叔,只有表演的小妹妹的数量不少与叔叔的数量的时候叔叔才会放她们过去。
为了节省开销,小妹妹快递公司希望派最少的小妹妹把女王大人的信件送到。请你告诉他们需要派几个小妹妹。
输入格式
输入第一行为数据组数T(T<=10),接下来T组数据,每组第一行为n,m,,2<=n<=10000,1<=m<=100000,表示Mays王国的道路由n个节点组成,接下来m行,每行一组u,v,c表示连接节点u,v的一条无向道路,且路障上有c个叔叔,1<=u,v<=n,0<=c<=100。女王大人和皇家小妹妹快递公司都在节点1,帅气的邻国王子住在节点n。
输出格式
每组数据输出一个数字,表示小妹妹快递公司最少需要派出的小妹妹数量。如果无论派出多少小妹妹都无法把信送到帅气的邻国王子手里,输出"shimatta!"。
输入样例
1
3 3
1 2 1
2 3 1
1 3 3
输出样例
1
这种题先把边排序,用并查集,加边时看1和n是否连接
#include <iostream>
#include <cstdio>
#include <algorithm>
#define maxn 10005
#define maxm 100005
using namespace std;
struct Edge
{
int value;
int a;
int b;
}edge[maxm];
int fa[maxn];
int n,m,t;
bool flag;
bool cmp(Edge x,Edge y)
{
return x.value<y.value;
}
int GetFather(int x)
{
if(fa[x]==x) return x;
fa[x]=GetFather(fa[x]);
return fa[x];
}
int main()
{
scanf("%d",&t);
while(t--)
{
flag=false;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].value);
sort(edge+1,edge+1+m,cmp);
for(int i=1;i<=m;i++)
{
fa[GetFather(edge[i].b) ]=GetFather(edge[i].a);
if(GetFather(1)==GetFather(n))
{
flag=true;
printf("%d\n",edge[i].value==0 ? 1: edge[i].value);
break;
}
}
if(!flag) printf("shimatta!\n");
}
return 0;
}
--------------------------------------------------------------------------------------------------------------
C. 学姐点名 2014新生暑假个人排位赛08
题目描述
学姐辛辛苦苦准备了一次讲座,讲课的过程中学姐数了一下人数,发现居然少到了一个学弟,学姐很生气,下决心要把这个学弟找出来。学姐开始点名了,为了快一点签到,学姐点名的时候只点大家的学号。学姐说:“这么简单的统计,几行就可以搞定,帮我写个程序吧,超过500B的运行时间又在1ms以上的我不要”。
oj的功能出了点问题,内存判定没有效果,代码长度限制也没有效果。oj上显示超过528B的代码,比赛结束前时限直接调成1ms手工重新判定。(换句话说,本地代码大小超过500B的AC不算。)
输入格式
每组数据第一行一个数N,接下来N-1行每行一个数字表示学弟的学号。
多组数据,eof结束。
2组数据N为1000000
500组数据满足N不大于3000
1000组数据满足N不大于10
输出格式
输出没到的学弟的学号
输入样例
3
1
3
2
1
输出样例
2
2
(既然卡长度了,就把所有的空格回车都去掉~~)
#include <iostream>
#include <cstdio>
using namespace std;
int n,i,t;long long ans;int main(){while(~scanf("%d",&n)){ans=0; for(i=1;i<n;i++){scanf("%d",&t);ans+=t;} printf("%lld\n",(long long)n*(long long)(n+1)/2-ans);}return 0;}
hahaha好短~
-----------------------------------------------------------------------------------------------
D. 解码锦标赛 2014新生暑假个人排位赛08
题目描述
明光村迎来了一年一度的盛世——解码锦标赛,有 2^N 次个队伍从世界各村赶来参与比赛,编号为 1 - 2^N。赛制为每一轮晋级一半队伍,按序号大小两两比赛,淘汰弱者。一轮结束后,所有的胜者进入下一轮,依旧是按顺序两两比赛。比如第一轮就是 1 vs 2, 3 vs 4 ... 2^N - 1 vs 2^N。在一旁围观的 Mays 学姐告诉你,N次比赛后的胜者是唯一的。现在你拿到了一份各个参赛队伍的对抗胜率表 win,为 2^N * 2^N 的矩阵, win[i][j] 为一位小数,代表i胜j的概率。
你能告诉 Mays 学姐最有可能获得世界冠军的是那支队伍吗?
输入格式
多组数据。每组第一行为 N ,N <= 8,接下来 N 行 N 列为对抗胜率矩阵。 保证 win[i][j] + win[j][i] = 1 (i != j)。 以 N=0 结束输入。
输出格式
对每组数据,输出胜率最大的队伍的序号。如果最大的两个概率相差不到 0.001,则认为胜率相等,输出序号最小者。
输入样例
2
0.0 0.1 0.2 0.3
0.9 0.0 0.4 0.5
0.8 0.6 0.0 0.6
0.7 0.5 0.4 0.0
2
0.0 0.8 0.1 0.4
0.2 0.0 0.2 0.6
0.9 0.8 0.0 0.3
0.6 0.4 0.7 0.0
0
输出样例
2
4
#include <iostream>
#include <cstdio>
using namespace std;
int twi[10]={1,2,4,8,16,32,64,128,256,512};
double dp[300][10];
double a[300][300];
int n;
double maxval;
int maxpos;
int main()
{
while(~scanf("%d",&n))
{
if(n==0) break;
for(int i=1;i<=twi[n];i++)
for(int j=1;j<=twi[n];j++)
scanf("%lf",&a[i][j]);
for(int i=1;i<=twi[n];i+=2)
{
dp[i][1]=a[i][i+1];
dp[i+1][1]=a[i+1][i];
}
for(int i=2;i<=n;i++)
{
for(int j=1;j<=twi[n];j+=twi[i])
{
for(int x=0;x<twi[i]/2;x++)
{
dp[j+x][i]=0;
for(int y=twi[i]/2;y<twi[i];y++) dp[j+x][i]+=dp[j+y][i-1]*a[j+x][j+y];
dp[j+x][i]*=dp[j+x][i-1];
}
for(int x=twi[i]/2;x<twi[i];x++)
{
dp[j+x][i]=0;
for(int y=0;y<twi[i]/2;y++) dp[j+x][i]+=dp[j+y][i-1]*a[j+x][j+y];
dp[j+x][i]*=dp[j+x][i-1];
}
}
}
/* for(int i=1;i<=n;i++)
{
for(int j=1;j<=twi[n];j++) printf("%.3lf ",dp[j][i]);
printf("\n");
}*/
maxval=0;
for(int i=1;i<=twi[n];i++)
if(dp[i][n]-maxval>0.001){maxval=dp[i][n];maxpos=i;}
printf("%d\n",maxpos);
}
return 0;
}
-------------------------------------------------------------------------------------------------------------------------------------------
E. 田田的算数题 2014新生暑假个人排位赛08
题目描述
焦级长和田田玩算数。焦级长给田田一个数列ai,然后问田田第i项到第j项的数的和。
田田随手写了个简单的数据结构就搞定了。于是焦级长打算加大题目难度。
焦级长又添加了一个操作,即从第i项到第j项都按顺序加上一个等差数列的项
如从第2项到第4项加入一个首项为1,公差为3的等差数列即第2项+1,第3项+4,第4项+7,以此类推
于是田田写了半天又WA又T还RE了一炮,终于来求救你让你来写个程序帮他
输入格式
第一行为case数T
每个case第一行输入n,m,表示有数列长为n,有m个操作
之后n个数为ai即数列的初始数
之后m行为m个操作
第一个操作为插入
为1,l,r,x,d
1表示插入操作,l,r为数列的[l,r]会进行操作,等差数列的首项为x,公差为d
第二个操作为查询和
为2,l,r
2表示查询,l,r为求和的范围,即求al到ar的数列的和
T<=10
n,m<=10^5
ai,x<10^5,|d|<=100;
输出格式
每个查询输出一个值
输入样例
1
6 9
1 2 3 4 5 6
2 1 4
2 1 6
1 1 4 1 0
2 2 5
1 2 4 1 1
2 1 4
2 2 4
2 1 5
2 2 6
输出样例
10
21
17
20
18
25
29
维护三个域 sum,x(首项),d(公差)
其中x和d是等差数列的关键元素(有了x,d和项数就能求出等差数列以及和)
而且等差数列有两个重要的性质
1.x和d具有叠加性质,两个相同项数的等差数列叠加,x就是两个x的和,d也是两个d的和,这样在传递时就像add数组一样可以叠加了
2.知道一个序列的x和d,可以求出其子序列的x和d,这样在传递懒标记时就能做到拆分整段了
所以,再做这种更新的每项不一样的线段树时,就找可以唯一确定这个序列,而且具有以上两个性质的几个值作为懒标记来维护sum值即可
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 100005
using namespace std;
struct Node
{
long long x;
long long d;
}add[4*maxn];
long long a[maxn],sum[4*maxn];
long long t,n,m;
void Build(long long o,long long l,long long r)
{
long long m=l+(r-l)/2;
if(l==r) sum[o]=a[l];
else
{
Build(o*2,l,m);
Build(o*2+1,m+1,r);
sum[o]=sum[o*2+1]+sum[o*2];
}
}
void update(long long o,long long l,long long r,long long y1,long long y2,long long x,long long d)
{
if(r==y2&&l==y1)
{
add[o].d+=d;
add[o].x+=x;
return;
}
sum[o]+=x*(y2-y1+1)+(y2-y1)*(y2-y1+1)/2*d;
long long mid=(r+l)/2;
if(y2<=mid)
{
update(o*2,l,mid,y1,y2,x,d);
return;
}
if(y1>mid)
{
update(o*2+1,mid+1,r,y1,y2,x,d);
return;
}
update(o*2,l,mid,y1,mid,x,d);
update(o*2+1,mid+1,r,mid+1,y2,x+(mid+1-y1)*d,d);
return;
}
long long query(long long o,long long l,long long r,long long y1,long long y2)
{
long long mid=(r+l)/2;
if(l==y1&&r==y2){
sum[o]+=add[o].x*(r-l+1)+(r-l)*(r-l+1)/2*add[o].d;
if(l!=r)
{
add[o*2].d+=add[o].d;
add[o*2+1].d+=add[o].d;
add[o*2].x+=add[o].x;
add[o*2+1].x+=add[o].x+(mid+1-l)*add[o].d;
}
add[o].d=0;add[o].x=0;
return sum[o];
}
if(add[o].x || add[o].d){
if(l!=r)
{
add[o*2].d+=add[o].d;
add[o*2+1].d+=add[o].d;
add[o*2].x+=add[o].x;
add[o*2+1].x+=add[o].x+(mid+1-l)*add[o].d;
}
sum[o]+=add[o].x*(r-l+1)+(r-l)*(r-l+1)/2*add[o].d;
add[o].x=0;add[o].d=0;
}
if(y2<=mid)
{
return query(o*2,l,mid,y1,y2);
}
if(y1>mid)
{
return query(o*2+1,mid+1,r,y1,y2);
}
return query(o*2,l,mid,y1,mid)+query(o*2+1,mid+1,r,mid+1,y2);
}
int main()
{
//freopen("ine.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);
for(long long i=1;i<=n;i++) scanf("%lld",&a[i]);
memset(sum,0,sizeof(sum));
for(long long i=1;i<=4*n;i++) {add[i].d=0;add[i].x=0;}
Build(1,1,n);
for(long long i=1;i<=m;i++)
{
long long Ord;
scanf("%lld",&Ord);
if(Ord==1)
{
long long l,r,x,d;
scanf("%lld%lld%lld%lld",&l,&r,&x,&d);
update(1,1,n,l,r,x,d);
}
else
{
long long l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",query(1,1,n,l,r));
}
}
}
return 0;
}
最近遇到了好多各种花样的线段树题,这儿再贴一个
【hdu4893】
这题要支持三个操作
1.把某点加上d
2.把一段区间改成最近的等差数列
3 查询一段的和
这题既有点更新又有区间更新,如果把区间更新当成点更新叠加的话,Omnlogn就超了
所以要对区间更新这一操作加懒标记
懒标记之下的点,表示到目前为止还没有用处,所以可以不更新(和实时的值不同)
懒标记之上的点要和实时的值相同
所以,我们设一个bool的懒标记(针对区间更新的)表示该点之下需要被改成fib值
但是,懒标记pushdown的时候,要更新该点的值为所有子节点改fib之后的和,而这个和无法一步求出来
所以再加一个域fv,记录这个和值,在修改单点的时候,随着递归的pushup把每个fv都顺势求出来
这样pushdown懒标记时就可以直接让和等于fv了!
这是建立域的另一个思路,先把区间更新的描述域做成懒标记,发现有些值要从底向上递推求出而无法直接得到时,加一个辅助域
ps,我们可以这样直观的理解懒标记
想象海边的涨潮,沙滩被打湿的边缘线就是懒标记,平时潮水就在这个线之下活动,所以数据保证是更新过的
一旦遇到更大的潮水要侵占干的沙滩,边缘线就要扩大,相当于懒标记下传
嗯,一些细节处理就想这个生动的模型就行了~
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define maxn 100005
#define inf 10000000000000000
#define ll __int64
using namespace std;
ll fib[90];
ll GetFib(ll x)
{
if(x==0) return 1;
if(x==1) return 1;
int i=1;
while(abs(x-fib[i])>abs(x-fib[i+1])) i++;
return fib[i];
}
struct Node
{
ll v;
ll fv;
bool mark;
}sum[4*maxn];
ll a[maxn];
ll n,m;
void Build(ll o,ll l,ll r)
{
ll m=(l+r)/2;
if(l==r) sum[o].fv=1;
else
{
Build(o*2,l,m);
Build(o*2+1,m+1,r);
sum[o].fv=sum[o*2+1].fv+sum[o*2].fv;
}
}
void change(ll o,ll l,ll r,ll y1,ll y2)
{
if(r==y2&&l==y1)
{
sum[o].mark=1;
return;
}
if(sum[o].mark){sum[o].v=sum[o].fv;sum[o].mark=false;sum[o*2].mark=true;sum[o*2+1].mark=true;}
ll mid=(r+l)/2;
if(y2<=mid)
{
change(o*2,l,mid,y1,y2);
}
else
if(y1>mid)
{
change(o*2+1,mid+1,r,y1,y2);
}
else
{
change(o*2,l,mid,y1,mid);
change(o*2+1,mid+1,r,mid+1,y2);
}
sum[o].v=(sum[o*2].mark?sum[o*2].fv:sum[o*2].v) + (sum[o*2+1].mark?sum[o*2+1].fv:sum[o*2+1].v);
return;
}
void update(ll o,ll l,ll r,ll k,ll d)
{
if(r==k&&l==k)
{
if(sum[o].mark) {sum[o].v=sum[o].fv;sum[o].mark=false;}
sum[o].v+=d;
sum[o].fv=GetFib(sum[o].v);
return;
}
if(sum[o].mark){sum[o].v=sum[o].fv;sum[o*2].mark=true;sum[o*2+1].mark=true;sum[o].mark=false;}
ll mid=(r+l)/2;
if(k<=mid)
update(o*2,l,mid,k,d);
else
update(o*2+1,mid+1,r,k,d);
sum[o].fv=sum[o*2].fv+sum[o*2+1].fv;
sum[o].v=(sum[o*2].mark?sum[o*2].fv:sum[o*2].v) + (sum[o*2+1].mark?sum[o*2+1].fv:sum[o*2+1].v);
return;
}
ll query(ll o,ll l,ll r,ll y1,ll y2)
{
ll mid=(r+l)/2;
if(l==y1&&r==y2)
{
if(sum[o].mark)
{
sum[o].v=sum[o].fv;
sum[o].mark=false;
if(l!=r)
{
sum[o*2+1].mark=true;
sum[o*2].mark=true;
}
}
return sum[o].v;
}
if(sum[o].mark){
if(l!=r)
{
sum[o].v=sum[o].fv;
sum[o].mark=false;
sum[o*2+1].mark=true;
sum[o*2].mark=true;
}
}
if(y2<=mid)
{
return query(o*2,l,mid,y1,y2);
}
if(y1>mid)
{
return query(o*2+1,mid+1,r,y1,y2);
}
return query(o*2,l,mid,y1,mid)+query(o*2+1,mid+1,r,mid+1,y2);
}
int main()
{
//freopen("ine.txt","r",stdin);
//freopen("out.txt","w",stdout);
fib[0]=1;
fib[1]=1;
for(int i=2;i<=89;i++) fib[i]=fib[i-1]+fib[i-2];
while(~scanf("%I64d%I64d",&n,&m))
{
for(ll i=1;i<=4*n;i++) {sum[i].fv=0;sum[i].v=0;sum[i].mark=false;}
Build(1,1,n);
for(ll i=1;i<=m;i++)
{
ll Ord;
scanf("%I64d",&Ord);
if(Ord==1)
{
ll k,d;
scanf("%I64d%I64d",&k,&d);
update(1,1,n,k,d);
}
else
if(Ord==2)
{
ll l,r;
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",query(1,1,n,l,r));
}
else
{
ll l,r;
scanf("%I64d%I64d",&l,&r);
change(1,1,n,l,r);
}
/*for(int i=1;i<=4*n;i++) printf("%lld ",sum[i].v);
printf("\n");
for(int i=1;i<=4*n;i++) printf("%lld ",sum[i].fv);
printf("\n");
for(int i=1;i<=4*n;i++) printf("%d ",sum[i].mark);
printf("\n");*/
}
}
return 0;
}
差不多理解线段树的特性了,以后就可以做各种花样更新啦~
(不要套白书的模板!!!!要用我这个版本的!!!白书太难懂了!!那个模板我不理解卡了我好几天改不出来!!!)
线段树+扫描线求矩形面积并,先记下来以后看一下~