「训练日志15」 (8.10)

T1 Blue

  一点都不简单,可是还是有很多巨佬$A$掉了。

  对于每只蛤,都应该向它所能到达的最远地方跳,蛤都相同,若有一只蛤跳向最远,那可能就会有另一只蛤无法超过他从而跳向较近的位置。而假设起先那只蛤跳到了较近的位置,另一只蛤还会跳向更远的位置,此时两只蛤互相交换了位置,而但是效果等价。

  贪心从前向后贪。

证明:

如果最右的蛤不往最远的石子跳,而是选择了一个较近石子那么必然会存在一个该蛤左边的蛤越过了它跳向其右边因为每个蛤的能力是相同的,我们可以交换路线使得贪心策略不变差。

接着用归纳法可以证明对于所有蛤该策略最优。

 

  然后就简单了,$set$模拟也可以,队列也可以,注意队列中的元素代表的是有效蛤所在的位置

  $set$模拟注意set中要插入0和最大的L,以保证STL的空间的安全(如$--s.begin()$)。

  STL中$lower_bound$是大于等于它的元素,$--$之后是小于,$upper_bound$是大于它的元素,$--$之后是小于等于。

  小弟不才。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<set>
 4 #define HZOI std;
 5 using namespace HZOI;
 6 int T,n,m,d,L,ans;
 7 set<int> s;
 8 inline int read();
 9 int main()
10 {
11 //    freopen("blue.in","r",stdin);
12 //    freopen("3.out","w",stdout);
13     T=read();
14 //    double t1=clock();
15     while (T--)
16     {
17         ans=0;
18         s.clear();
19         n=read(),m=read(),d=read(),L=read();
20         for (int i=1; i<=n; ++i)
21             s.insert(read());
22         s.insert(0),s.insert(L);
23         if (d==L)
24         {
25             puts("Excited");
26             continue;
27         }
28         if (n==L-1)
29         {
30             if (d>=m) puts("Excited");
31             else printf("%d\n",d);
32             continue;
33         }
34         for (int i=1; i<=m; ++i)
35         {
36             int pos=0,flag=1;
37             int to;
38 //            printf("i=%d=======\n",i);
39             do
40             {
41                 to=*--s.upper_bound(pos+d);
42 //                printf("pos=%d to=%d\n",pos,to);
43                 if (to<=pos or to-pos>d) {flag=0;break;}
44                 if (to>=L) {++ans;break;}
45                 else pos=to,s.erase(to);
46             }while (pos);
47             if (!flag) break;
48         }
49         if (ans==m) puts("Excited");
50         else printf("%d\n",ans);
51     }
52 //    double t2=clock();
53 //    printf("%.9lf\n",t2-t1);
54 }
55 inline int read()
56 {
57     int nn=0; char cc=getchar();
58     while (cc<'0' or cc>'9') cc=getchar();
59     while (cc>='0' and cc<='9') nn=(nn<<3)+(nn<<1)+(cc^48),cc=getchar();
60     return nn;
61 }
Blue(set)
 1 #include<cstdio>
 2 #define HZOI std;
 3 using namespace HZOI;
 4 const int N=1e6+3;
 5 int T,n,m,d,L,l,r;
 6 int a[N],q[N<<1];
 7 inline int read();
 8 int main()
 9 {
10     T=read();
11     while (T--)
12     {
13         n=read(),m=read(),d=read(),L=read();
14         for (int i=1; i<=n; ++i)
15             a[i]=read();
16         l=r=0;
17         for (int i=1; i<=m; ++i) q[++r]=0;
18         for (int i=1; i<=n; ++i)
19         {
20             while ((l^r) and q[l+1]+d<a[i]) ++l;
21             ++l,q[++r]=a[i];
22         }
23         while ((l^r) and q[l+1]+d<L) ++l;
24         if (r-l==m) puts("Excited");
25         else printf("%d\n",r-l);
26     }
27 }
28 inline int read()
29 {
30     int nn=0; char cc=getchar();
31     while (cc<'0' or cc>'9') cc=getchar();
32     while (cc>='0' and cc<='9') nn=(nn<<3)+(nn<<1)+(cc^48),cc=getchar();
33     return nn;
34 }
Blue(队列)

T2 Weed

   线段树,考验对线段树维护信息的理解。

  我是按照学长的题解码的,学长写的很好,很明白了。

  要是在网上搜这篇题解,会有“这不是一道板子题吗”的嘲讽。

  具体,我先引用一下学长的课件:

•实际上,每个加数和删除的操作可以看作是入栈和弹栈操作,之后可以用线段树维护多个操作间的关系。线段树的下标是操作时间,由于我们想得到整个序列经过修改操作后的结果,因此线段树上维护四个信息:
s:区间内加数总和(仅考虑区间内部影响);
nd:当前区间向前删除数字的数量;
na:当前区间内“净”加的元素数。
sd:当前区间被右兄弟删除后的总和。 ※仅对左儿子维护此信息。
•我们通过维护以上四个标记,就可以得到答案(root->s)。现在我们考虑如何维护信息。

首先,在建树或修改时,只需要将叶子节点的前三个标记维护好即可;在递归返回时,再计算最后一个。如果左儿子不够右儿子删,那么非常简单,直接利用这几个标记计算即可,l->sd=0。
比较麻烦的是左儿子不被删光的情况。我们先实现一个函数cal(x),利用sd标记计算在当前区间中删去x个元素后的和。

考虑cal(x)的实现(当前节点为o):
• 1.若r->na>x,那么返回l->sd+r->cal(x)。
• 2.若r->na<x,那么右儿子不够删,返回l->cal(x-r->na+r->nd)
• ※这里非常重要,一定要再删掉右儿子要删的
• 3.若r->na==x,直接返回l->sd。
有了这个函数,我们就可以非常方便地计算左儿子的sd,维护其他标记了,不再赘述。

• 由于这个奇怪的函数在每层节点都会调用,而一共有O(logn)层节点,所以线段树操作的时间复杂度变为O(log 2 n),总时间复杂度也比常规的线段树多一个log。
• 难点主要在于使用额外的函数维护信息,并正确的讨论各种情况。
• 考试时一定要想清楚,时间复杂度是靠“链状”延伸的维护函数保障的,如果不小心写成了两边都下去,就变成O(n 2 )了。

  好的搬完了溜了。

  我们来分析一下这个题解。

  主要是在于信息的理解和$cal$函数的实现。

  $na$表示“净”增加,求出来的是当前区间受到自己区间内部的影响而得出来的净增加,$nd$表示“净”减少,求出来的是当前区间受到自己区间内部影响的净减少,所以,$na$可以被作用与之后的区间,$nd$可以作用于之前的区间,这就是他们的关系,由此合并信息的时候也就不难合并了,要想好区间本身、区间与区间的关系。

  $s$表示总数,$sd$表示左儿子受到右儿子影响后得到的总数,所以s只考虑内部贡献,而$sd$考虑的是一区间对另一区间的贡献。

  主要难点在于区间对区间的贡献影响是相对于区间而言的

  $cal$函数的具体实现方式,师兄的博客中也已经很清楚了,不再多说。

  $cal$函数的实现很巧妙,有效的作出了线段树本身没有的操作。

  具体实现代码中展示。

  小弟不才。

 1 #include<cstdio>
 2 #define HZOI std;
 3 using namespace HZOI;
 4 const int N=2e6+3;
 5 struct Tree{
 6     int l,r,na,nd,sm,smd;
 7 }tr[N<<2];
 8 int m,Q,t;
 9 void Build(int ,int ,int );
10 void Change(int ,int ,int ,int );
11 int Cal(int ,int );
12 inline int read();
13 int main()
14 {
15     m=read(),Q=read();
16     Build (1,1,m);
17     for (int i=1,x,y; i<=m; ++i)
18         x=read(),y=read(),Change(1,i,x,y);
19     for (int i=1,x,y,z; i<=Q; ++i)
20     {
21         x=read(),y=read(),z=read();
22         Change(1,x,y,z);
23         printf("%d\n",tr[1].sm);
24     }
25     return 0;
26 }
27 int Cal(int k,int x)
28 {
29     if (tr[k].l==tr[k].r) return x>=tr[k].na?0:tr[k].sm;
30     if (tr[k<<1|1].na==x) return tr[k<<1].smd;
31     else if (tr[k<<1|1].na<x) return Cal(k<<1,x-tr[k<<1|1].na+tr[k<<1|1].nd);
32     else if (tr[k<<1|1].na>x) return Cal(k<<1|1,x)+tr[k<<1].smd;
33 }
34 void Change(int k,int pos,int opt,int weight)
35 {
36     if (tr[k].l==tr[k].r)
37     {
38         if (opt) tr[k].nd=weight,tr[k].sm=tr[k].na=0;
39         else tr[k].na=1,tr[k].sm=weight,tr[k].nd=0;
40         return ;
41     }
42     int mid=tr[k].l+tr[k].r>>1;
43     if (pos<=mid) Change(k<<1,pos,opt,weight);
44     else Change(k<<1|1,pos,opt,weight);
45     tr[k].nd=tr[k<<1].nd+((tr[k<<1|1].nd-tr[k<<1].na)>0?(tr[k<<1|1].nd-tr[k<<1].na):0);
46     tr[k].na=tr[k<<1|1].na+((tr[k<<1].na-tr[k<<1|1].nd)>0?(tr[k<<1].na-tr[k<<1|1].nd):0);
47     if (tr[k<<1].l==tr[k<<1].r)
48     {
49         if (tr[k<<1|1].nd>=1) tr[k<<1].smd=0;
50         else tr[k<<1].smd=tr[k<<1].sm;
51     }
52     else
53     {
54         if (tr[k<<1|1].nd>=tr[k<<1].na) tr[k<<1].smd=0;
55         else tr[k<<1].smd=Cal(k<<1,tr[k<<1|1].nd);
56     }
57     tr[k].sm=tr[k<<1].smd+tr[k<<1|1].sm;
58 }
59 void Build(int k,int l,int r)
60 {
61     tr[k].l=l,tr[k].r=r;
62     if (l==r) return ;
63     int mid=l+r>>1;
64     Build(k<<1,l,mid);
65     Build (k<<1|1,mid+1,r);
66 }
67 inline int read()
68 {
69     int nn=0; char cc=getchar();
70     while (cc<'0' or cc>'9') cc=getchar();
71     while (cc>='0' and cc<='9') nn=(nn<<3)+(nn<<1)+(cc^48),cc=getchar();
72     return nn;
73 }
Weed

 

T3 Drink

  暴力可过,不明原因,循环展开。

  小弟不才。

 

 1 #include<cstdio>
 2 #define HZOI std;
 3 using namespace HZOI;
 4 int n,m,Q,g,h;
 5 char a[2003][2003],cc1,cc2,cc3,cc4;
 6 int main()
 7 {
 8     scanf("%d%d%d",&n,&m,&Q);
 9     for (int i=1; i<=n; ++i)
10         for (int j=1; j<=m; ++j)
11         {
12             a[i][j]=getchar();
13             while (a[i][j]<'0' or a[i][j]>'9') a[i][j]=getchar();
14         }
15     for (int q=1,x,y,z; q<=Q; ++q)
16     {
17         scanf("%d%d%d",&x,&y,&z);
18         for (int k=z; k>0; k-=2)
19         {
20             for (int i=0; i<k-1; ++i)
21             {
22                 int tx1=x,ty1=y+i;
23                 int tx2=x+i,ty2=y+k-1;
24                 int tx3=x+k-1,ty3=y+k-1-i;
25                 int tx4=x+k-1-i,ty4=y;
26                 cc1=a[tx1][ty1];
27                 cc2=a[tx2][ty2];
28                 cc3=a[tx3][ty3];
29                 cc4=a[tx4][ty4];
30                 a[tx4][ty4]=cc3;
31                 a[tx3][ty3]=cc2;
32                 a[tx2][ty2]=cc1;
33                 a[tx1][ty1]=cc4;
34             }
35             ++x,++y;
36         }
37     }
38     for (int i=1; i<=n; ++i)
39     {
40         for (int j=1; j<=m; ++j)
41             putchar(a[i][j]),printf(" ");
42         puts("");
43     }
44     return 0;
45 }
View Code

 

 

 

 

转载于:https://www.cnblogs.com/LH-Xuanluo/p/11333780.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值