写点杂七杂八的流水帐…
最近准备noip做了好几套模拟题…题目一般比较简单,没什么必要说就没写出来了。
今天叉姐指导我要在信心上认为“很难挂”,同时要在心理上相当的重视…我觉得还是相当有道理的,我往往是保持适度的紧张才会考的比较好…
另今天看到的天津赛区的题目解压密码:orzrobahougong3000duowan…(大小写有出入)感到非常泪流满面…最后SJTU第一的超了清华的2题并成功AK,不得不膜拜啊!
另外一位好阳光的学妹推荐了一部漫画《长安幻夜》,国产的,看了几章感觉还蛮不错,就是准备继续看…不过她好像感冒了,虽然不严重…希望她能快快好起来吧~
另外MS我的小徒也感冒了…
恩,突然又想起叉姐的签名“找个妹子当面泡”,我就不发表评论了…
今天初中班主任也跑来一聚,都好开心。然后吹水。某同学吐嘈了一下深圳中学强大的奥数,让我顿生怎么又是那儿的感觉…
突然发现我空间里写心情的少得可怜…从今天起记录些感觉吧…当然,我觉得比较不错的题目还是会放上来的…
就这样,睡觉…
分类存档:默认分类
[BZOJ2006 [NOI2010]超级钢琴]【RMQ】【二叉堆】【区间裂解】
【题目链接】http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=2006
【算法分析】
就是搞成前缀和数组以后写一个rmq。然后在外面套一个二叉堆每次取最大值。然后把取出的区间裂解成两半,去掉中间那个最小值元素,继续放进二叉堆里,这样就可以AC。
T_T时间排在第3名。前面两个都是JZP神牛。
【时间复杂度】O((n+m) lg (n+m))
【空间复杂度】O(n lg n + n+m)
【吐槽】
NOI的时候写得二分答案+平衡树被卡得只剩30分T_T。。。
现在高3一个月只能回一次家。。。本次是因为配眼镜为由请假回家,就找些题练练手= =,准备我巨重要的NOIP。
【CODE】
#include
typedef __int64 lld;
const int N=500005;
int n,m,low,high;
int a[N];
int lg[N];
int zz[19][N];
lld ST[19][N];
int Get(int l,int r){
int k=lg[r-l+1];
if (ST[k][l]
struct Heap_t{
struct Node{int l,r,i;lld key;}Q[N+N];
int tot;
void up(int k){
while (k>1 && Q[k>>1].key swap(Q[k>>1],Q[k]);
k>>=1;
}
}
void down(int k){
int p;
while (k<<1<=tot){
p=k<<1;
if (p
swap(Q[p],Q[k]);
k=p;
}
else break;
}
}
void Insert(int l,int r,int i){
if (l>r) return;
tot++;
Q[tot].l=l; Q[tot].r=r; Q[tot].i=i; Q[tot].key=ST[0][i]-ST[0][Get(l,r)];
up(tot);
}
void Del(){
Node tmp=Q[1];
Q[1]=Q[tot–];
down(1);
int pos=Get(tmp.l,tmp.r);
Insert(tmp.l,pos-1,tmp.i);
Insert(pos+1,tmp.r,tmp.i);
}
void init(){
int i;
for (tot=0,i=1;i<=n;i++)
Insert(max(0,i-high),i-low,i);
}
void solve(){
lld ans=0;
while (m){
ans+=Q[1].key;
Del();
m–;
}
printf("%I64dn",ans);
}
}Heap;
void init(){
scanf("%d%d%d%d",&n,&m,&low,&high);
int k,s,i;
for (i=1;i<=n;i++) scanf("%d",&a[i]);
for (i=1;i<=n;i++) {ST[0][i]=ST[0][i-1]+a[i]; zz[0][i]=i;}
for (k=1;1<
if (ST[k-1][i]
zz[k][i]=zz[k-1][i];
}
else{
ST[k][i]=ST[k-1][i+s];
zz[k][i]=zz[k-1][i+s];
}
}
for (i=1,k=0;i<=n;i++)
lg[i]=k+=(i==(1<
int main(){
init();
Heap.init();
Heap.solve();
}
[ZOJ3385 Hanami Party]【贪心】【栈】★★
【题目大意】
一开始主角有一个能力值L,然后有n天,每天他可以执行下列两个操作中的其中一个:
1:自身能力值+1
2:制造与自己能力值相等个单位的食物。
另外,每天食物都会被饿鬼吃掉一些,但是主角又不能惹怒这个饿鬼。
求在不惹怒这个饿鬼的情况下,最后剩余食物最多能是多少?
如果必须惹怒,那么输出Myon。
【算法分析】
下面的 1 2分别表示操作1和操作2。
本题可以利用贪心来解决。
我们的策略应该是尽量升级,然后必须造食物才造。就是升级的尽量前推。
因为如果出现 2,1这种情况,而1和2可以交换而使得没有出现饿鬼被惹怒的情况,那么显然交换以后能造多一个单位的食物。所以升级是应该被尽量前推的。
可以发现其实我们是将这个操作分组,分成这样的数段:(1,2,2…2),(1,2,2…..2),(1,2,2….2)
(一开始在0天时我们看做操作1)
现在我们只关心操作1的划分。
为了实现我们的策略,那么我们对于前i天,都求出最多所能进行操作1多少次。
这可以用一个栈来解决。
这个栈里面存的是每次操作1所发生的时间。
然后每次把今天是操作1放进栈内。
如果栈尾的元素到今天不能满足饿鬼的需求,那么退栈,
这样我们维护的一个栈是所有升级操作都尽量前推好的了。并且到了第i天所能升级最多次的。
当然,如果栈里连第0天那个虚拟的升级都被退了,那么就无解了。
、
、
、
、
现在可能还有一些疑问,那就是升级多了固然会把饿鬼惹怒,但是有时候升级少了我们也会把饿鬼惹怒的。
比如说一开始能力值为0,然后你造食物多少天也没用。这样还是会惹怒饿鬼。
就是说,不能保证退栈以后依然是可行解。
、
、
、
、
现在我们再来看看一个东西。
假如说,我们退栈导致某个地方饿鬼不爽了。
那么我们从这个饿鬼不爽的地方分开来看。
假设饿鬼不爽这一天为j。
对于我们现在的第i天来讲,我们在第j天时,退栈前,能力强,剩余的食物也多。
(因为栈的大小代表升级的次数,并且原来饿鬼在第j天时爽的,退栈以后不爽,可以说明剩余食物变少了)
那么既然这样,如果会使得该解退栈后成为非可行解的,对于第i天来讲,是有害无利。
但是退栈的条件是升级这么多不行了。
所以,其实如果走出了这一步退栈,实际就是无解了。
我们会一直退到栈空为止。判为无解。
、
、
、
、
做完了第n天以后,我们还要把所有元素退栈,因为到了后面可能造食物更为划算。
然后这里退栈造成的非可行解问题,我们上面已经说明,如果是造成了非可行解,那么答案只会更差。
而我们取的最优值,所以一起判断也没关系。
然后,我们就得出了这个O(n)的贪心算法。
【其它】
泪流满面,3Y。原因是这个ZOJ的int64。。。居然这么标准,和NOI一样用long long+%lld。。。一开始就搞%I64d。搞挂了。
其实一开始我是看了一下月赛的Ranklist,发现最后一题好像每个人都A了。。。
然后想去切一下准备NOIP。。。
切了好一会儿终于切掉该题,感觉怎么现在的人都这么强T_T,这种题成为众人秒杀类题目。
颓然发现,由于题目过多,他那个Ranklist在我的浏览器下,下面还有横条。原来我看到众人秒杀的是倒数第二题。。。而最后一题只有navi大神AC。。。
【CODE】
#include
typedef long long lld;
const lld N=100005;
lld n,init_L,Qt;
lld a[N],Q[N],Rest_Food[N];
lld Sum[N];
int main(){
while (scanf("%lld%lld",&n,&init_L)!=EOF){
lld i,j,ans;
for (i=1;i<=n;i++) scanf("%lld",&a[i]);
for (Sum[0]=0,i=1;i<=n;i++) Sum[i]=Sum[i-1]+a[i];
Qt=Rest_Food[0]=Q[0]=0;
for (i=1;i<=n;i++){
Qt++;
Q[Qt]=i;
Rest_Food[Qt]=Rest_Food[Qt-1]+(init_L+Qt-1)*(i-Q[Qt-1]-1)-(Sum[i]-Sum[Q[Qt-1]]);
while (Qt>=0 && Rest_Food[Qt]+(i-Q[Qt])*(init_L+Qt)
}
if (Qt<0) puts("Myon");
else{
ans=0;
while (Qt>=0){
if (Rest_Food[Qt]+(n-Q[Qt])*(init_L+Qt)-(Sum[n]-Sum[Q[Qt]])>ans)
ans=Rest_Food[Qt]+(n-Q[Qt])*(init_L+Qt)-(Sum[n]-Sum[Q[Qt]]);
Qt–;
}
printf("%lldn",ans);
}
}
}
[ZOJ3365 Integer Numbers]【排序】【二分】
【题目大意】
给定一个序列,让你改变最少的数,使之变成形如这样的模式x,x+1,x+2,x+3….x+n。
【算法分析】
不考虑改变什么数,只考虑不用改变什么数,如果不用改变的话,那么当且仅当i-a[i]为同样的值。
而且最终结果不可能全都改变,所以可以以i-a[i]把他们分类,个数最多的一类就可以作解,最后输出即可。
这里面用到类似离散化的思想,中途用二分找位置。当然,hash也是可以的。
【其它】1Y
【CODE】
#include
const int N=50000;
int n;
int a[N];
int F[N],L[N];
void solve(){
int l,r,mid,i,ans=0,Maxi;
for (i=0;i
while (l
if (L[mid]
}
F[l]++;
}
for (i=0;i
ans=F[i];
Maxi=i;
}
printf("%dn",n-ans);
for (i=0;i
printf("%d",i-L[Maxi]);
}
putchar(‘n’);
}
int main(){
while (scanf("%d",&n)+1){
int i;
for (i=0;i
L[i]=i-a[i];
F[i]=0;
}
sort(L,L+n);
solve();
}
}
[ZOJ3362 Beer Problem]【最大费用流】【Not 最大费用最大流】【流中无向边与有向边的详细YY】
【题目大意】
1是源点,其它的n-1个点都可以流出。每滴水所得的费用w[i]。
然后给出一些无向边,流走过这些边,每滴水要消耗费用,并且只能有c滴水通过这条路。
就是比如给定(x,y),容量为c。
从x到y流过f1滴水,从y到x流过f2滴水,那么f1+f2<=c。
求怎样得到最大利润。
【算法分析】
把那n-1个点汇到一个超级汇中作最大费用最大流就行了。
至于无向边的问题,我们只要加两条有向边就行了。
大牛可以无视下面这个入门问题。。。= =
那么我们现在看一下为啥加两条有向边是对的。
假设一条无向边(x,y),他允许通过流量为c。
我们现在搞成两个有向边。
1、x->y,容量为c
2、y->x,容量为c
我们再假设最终完成一个流以后,x->y的流为f1,y->x的流为f2,由于对称,不妨设f1>=f2。
那么本质可以变成x->y的流为f1-f2,y->x的流为0。
我们这样考虑:
本来有f2的流S->y->x->T。
同时有f1的流S->x->y->T,并有f1>=f2。
那么现在S->y上的流有f2这么多被分到y->x上了,我们现在把他扯回来。存在y结点上。
于是y点存的水e(y)=f2
再把S->x->y的流扯回来,存在x结点上。
于是x点存的水e(x)=f1
然后现在观察发现。
x->T部分和原来相比,缺了f2,需要补充f2的流来满足流量守恒。
y->T部分和原来相比,缺了f1,需要补充f1的流。
然后我们把e(x)中f1的流分配给x->T这条路,那么分完以后e(x)=f1-f2。
然后再把e(y)中f2的流分配给y->T这条路,那么分完以后e(y)=0。并且y->T这条路还缺少f1-f2的流补充。
于是最后,把e(x)中的流,通过x->y,补给到y->T这条路上。
就完成了这个转化。
所以,凡是x->y中有流,且y->x中也有流的结果,我们都可以转化为只有一条有流的结果。
并且边上的流都小于等于原来的。
这样,就证明了这样加边最后必然与合法解所对应,如果要输出方案的话,把所有这种对应边的流量相互抵消以后输出,就可以得到满足条件的解。
/
/
/
/
/
但是,我们只是证明了无费用的情况。
如果按照这样转化总费用是会变化的!
对于(x,y)这条边而言,一开始取了f1+f2个单位的费用,而转化以后只取了f1-f2的费用。
我们现在再分情况仔细YY一下:
1、求最大费用流,如果该无向边权值>0,显然无法求。。。就像有正权环的最长路一样。
但是如果该无向边的权值<0,显然按照上面的推导,f1+f2>f1-f2。(假设f1>=f2 && f1,f2>0)。
我们的费用流不会这么笨去取那个更差的解。
2、求最小费用流,基本上面的词取反义词就是了T_T。
还有,本题求的是最大费用流,流不一定要最大,只要流满足限制即可。我们找增广路如果找到增广费用小于等于0,就可以马上停止了。
如果是最大费用最大流,找增广路的话,要找到找不到为止。
【其它】1Y。呃~希望有同样困惑的童鞋们看完可以清楚一点。
【CODE】
#include
const int N=105;
const int E=40000;
const int INF=0x7FFFFFFF;
struct edge{int x,y,c,w;edge *next,*op;}g[E],*ls[N],*fa[N];
int m,n,S,T,e,cost;
int d[N],v[N],L[N];
void addedge(int x,int y,int c,int w){
g[++e].x=x; g[e].y=y; g[e].c=c; g[e].w= w; g[e].next=ls[x]; ls[x]=&g[e]; g[e].op=&g[e+1];
g[++e].x=y; g[e].y=x; g[e].c=0; g[e].w=-w; g[e].next=ls[y]; ls[y]=&g[e]; g[e].op=&g[e-1];
}
void init(){
for (int i=1;i<=n+1;i++) ls[i]=NULL;
e=-1; S=1; T=n+1;
for (int w,i=2;i<=n;i++){
cin >> w;
addedge(i,T,INF,w);
}
for (int x,y,c,w,i=1;i<=m;i++){
cin >> x >> y >> c >> w;
addedge(x,y,c,-w);
addedge(y,x,c,-w);
}
}
bool spfa(){
for (int i=S;i<=T;i++){
d[i]=-INF;
v[i]=0;
}
d[S]=0; v[S]=1; L[1]=S;
int head=0,tail=1;
while (head!=tail){
head=(head+1)%N;
for (edge *t=ls[L[head]];t;t=t->next)
if (t->c && d[t->x]+t->w>d[t->y]){
d[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
L[tail=(tail+1)%N]=t->y;
}
}
v[L[head]]=0;
}
return d[T]>0;
}
void change(){
int nf=INF;
for (int i=T;i!=S;i=fa[i]->x)
nf=min(nf,fa[i]->c);
cost+=nf*d[T];
for (int i=T;i!=S;i=fa[i]->x){
fa[i]->c-=nf;
fa[i]->op->c+=nf;
}
}
void MCMF(){
cost=0;
while (spfa()) change();
cout << cost << 'n';
}
int main(){
ios::sync_with_stdio(false);
while (cin >> n >> m){
init();
MCMF();
}
}