CSPS模拟94

我好菜啊。。。。。。

%%%迪神AK

虽然考试成绩不太好,但至少能想到正解了,也不会菜到打不出暴力。

T1:想了半天不会,发现直接打高精可以拿到80分,就赶紧码完扔了,结果正解是利用double避免了高精运算

解法:%%迪神,高精压位,同时只记录前300位进行比较。

or利用double和除法,最后和1比较

or正解:double,对所有数取log再进行比较

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define LL double
 7 #define N 1000050
 8 using namespace std;
 9 int x,y;
10 inline void work1()
11 {
12     LL a1=1,a2=0.0;
13     a1=1.0*y*log(x);
14     for(int i=1;i<=y;++i)a2+=1.0*log((double)i);
15     if(a1>a2)puts("No");
16     else puts("Yes");
17 }
18 int main()
19 {
20     freopen("yuuutsu.in","r",stdin);
21     freopen("yuuutsu.out","w",stdout);
22     int T;scanf("%d",&T);
23     while(T--)
24     {
25         scanf("%d%d",&x,&y);
26         work1();
27     }
28     fclose(stdin);fclose(stdout);
29 }
View Code

T2:考场正解,细节打炸。将原数列差分掉,然后利用就可以将下标mod k进行操作了。

注意特判:差分的区间为左闭右开。所以当右端点为n+1时是可以随意加的。然而当左端点为0时不能随便加,因为是左闭右开呀

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define LL long long
 6 #define N 2000050
 7 #define re register 
 8 using namespace std;
 9 LL s1[N];
10 inline int read()
11 {
12     int s=0,b=0;char c=getchar();
13     while(c>'9'||c<'0'){if(c=='-')b=1;c=getchar();}
14     while(c>='0'&&c<='9')s=s*10+c-'0',c=getchar();
15     if(b)return -s;
16     return s;
17 }
18 int n,k,m;
19 int a[N],al,pd[N];
20 inline void add(int pos,int val)
21 {
22     pos%=k;
23     if(pd[pos])return;
24     if(s1[pos]==-val){s1[pos]=0;--al;return;}
25     if(!s1[pos]){s1[pos]=val;++al;return;}
26     s1[pos]+=val;
27 }
28 int main()
29 {
30     freopen("august.in","r",stdin);
31     freopen("august.out","w",stdout);
32     n=read(),k=read(),m=read();
33     for(register int i=1;i<=n;++i)a[i]=read();
34     for(register int i=n;i;--i)
35         a[i]-=a[i-1],s1[i%k]+=a[i];
36     pd[(n+1)%k]=1;
37     for(int i=0;i<k;++i){
38         if(pd[i])continue;
39         if(s1[i])++al;
40     }
41     if(al)puts("No");
42     else puts("Yes");
43     for(int i=1,x,y;i<=m;++i)
44     {
45         x=read();y=read();
46         if(y){add(x,y);add(x+1,-y);}
47         if(al)puts("No");
48         else puts("Yes");
49     }
50     fclose(stdin);fclose(stdout);
51 }
左端点为0不能加

T3:这题好好写一下。

考场上先打出了暴力,然后考虑加优化。

暴力:

1 for(int i=1,lca;i<=n;++i)
2 {
3     lca=a[i];ans+=c[lca];
4     for(int j=i-1;j;--j){
5         lca=LCA(lca,a[j]);
6         ans+=c[lca];
7     }
8 }

暴力枚举右端点,发现左端点在一段区间内时lca是一定的。

那么这个东西可以用链表来维护。

链表记录lca的值(id)和这个lca对应左端点的数量(ct)

统计答案直接ct*id即可。

然而这种打法还是会被一条链的数据卡掉

深入挖掘它的性质。每插入一个新的右端点,会把链表内所有节点更新到新端点的祖先链上。(显然

那么新加入的节点就一定会加到链表的队尾。且设当前加入的为a[j],那么a[j]和a[j-1]的lca之前的点是共用的。

那么不断利用a[j]和a[j-1]的lca去弹掉链表的队尾。最后在队尾加入a[j]和a[j-1]的lca和a[j]即可

考虑在每个节点再维护一个答案的前缀和(an)

a[j]和a[j-1]的lca之前的所有的节点内所有东西(id,ct,an)都不变。

那么直接更新a[j]和a[j-1]的lca和a[j]的答案即可。

流程:

新开节点,插入队尾,在队中删除不合法的节点并更新a[j]和a[j-1]的lca控制区间长度。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define re register 
 4 #define N 200050
 5 using namespace std;
 6 inline int read()
 7 {
 8     int s=0,b=0;char c=getchar();
 9     while(c>'9'||c<'0'){if(c=='-')b=1;c=getchar();}
10     while(c>='0'&&c<='9')s=s*10+c-'0',c=getchar();
11     if(b)return -s;
12     return s;
13 }
14 int n,m,a[N],c[N],kkk,num;
15 struct node{int pre,ne,ct,id;LL an;}li[N*10];
16 LL ans;
17 int he[N],ne[N],to[N],fa[N],tot;
18 inline void addedge(int x,int y){to[++tot]=y;ne[tot]=he[x];he[x]=tot;}
19 int tp[N],dep[N],dfl[N],dfr[N],sz[N],hs[N],cnt;
20 int po[N];
21 inline void dfs1(int g)
22 {
23     dep[g]=dep[fa[g]]+1;sz[g]=1;
24     for(int i=he[g];i;i=ne[i])
25     {
26         dfs1(to[i]);
27         if(sz[to[i]]>sz[hs[g]])hs[g]=to[i];
28         sz[g]+=sz[to[i]];
29     }
30 }
31 inline void dfs2(int g)
32 {
33     dfl[g]=dfr[g]=++cnt;
34     po[cnt]=g;
35     if(hs[fa[g]]==g)tp[g]=tp[fa[g]];
36     else tp[g]=g;
37     if(hs[g])dfs2(hs[g]);
38     for(int i=he[g];i;i=ne[i])
39         if(to[i]!=hs[g])dfs2(to[i]);
40     dfr[g]=cnt;
41 }
42 inline int LCA(int x,int y)
43 {
44     if(dep[x]>dep[y])swap(x,y);
45     if(dfl[x]<=dfl[y]&&dfr[x]>=dfl[y])return x;
46     while(tp[x]!=tp[y])
47     {
48         if(dep[tp[x]]>dep[tp[y]])swap(x,y);
49         y=fa[tp[y]];
50     }
51     if(dep[x]>dep[y])return y;
52     else return x;
53 }
54 inline void del(int x)
55 {
56     int a1=li[x].pre,a2=li[x].ne;
57     if(a1)li[a1].ne=a2;
58     if(a2)li[a2].pre=a1;
59     if(!a2)kkk=a1;
60 }
61 int main()
62 {
63     freopen("sagittarius.in","r",stdin);freopen("sagittarius.out","w",stdout);
64     n=read();
65     for(int i=2;i<=n;++i){fa[i]=read();addedge(fa[i],i);}
66     dfs1(1);dfs2(1);
67     for(int i=1;i<=n;++i)a[i]=read();
68     for(int i=1;i<=n;++i)c[i]=read();
69     ans+=c[a[1]];li[++num].ct=1;
70     li[num].id=a[1];kkk=num;
71     li[num].an=c[a[1]];
72     for(int j=2;j<=n;++j){
73         li[++num].id=a[j];li[num].ct=1;
74         li[kkk].ne=num;li[num].pre=kkk;
75         kkk=num;int i,t;
76         for(i=li[kkk].pre;i;i=li[i].pre){
77             t=LCA(li[i].id,li[li[i].ne].id);
78             if(t!=li[i].id){li[i].id=t;
79                 if(li[i].id==li[li[i].ne].id){
80                     li[i].ct+=li[li[i].ne].ct;
81                     del(li[i].ne);
82                 }
83             }
84             else {
85                 if(li[i].id==li[li[i].ne].id){
86                     li[i].ct+=li[li[i].ne].ct;
87                     del(li[i].ne);
88                 }
89                 break;
90             }
91         }
92         if(!i)i=1;
93         for(;i;i=li[i].ne)li[i].an=li[li[i].pre].an+1ll*c[li[i].id]*li[i].ct;
94         ans+=li[kkk].an;
95     }
96     cout<<ans<<endl;
97 }
代码和题解有些出入,但本质上是一样的

复杂度证明:在每个点最多插入两个节点,每个节点被删除一次。复杂度为O(n)

但由于还要求lca,复杂度变为O(nlogn)。(然而你用tarjan求lca做到O(n)我也没意见)

 

posted @ 2019-10-30 21:13  真-不能AKt♞  阅读(190)  评论(4编辑  收藏  举报