模拟12 题解

A. 斐波那契(fibonacci)

首先想到a,b<=1e6的暴力:建树,直接向上标记求lca。

建树的过程中发现一个性质。

斐波那契第n代兔子,是n-2代及以前的兔子的儿子。

因为编号连续且与父亲编号大小有关,

设该节点的编号为$x$,在第$k$代,

则$f(x)=x-fib(k-1)$。

二分查找父亲,向上标记求lca即可。

(特判不换行,爆零两行泪)

(见代码第25行)

 1 #include<iostream>
 2 #include<cstdio>
 3 #define ll long long
 4 using namespace std;
 5 ll fib[70],a,b;
 6 int m;
 7 inline ll read(){
 8     register ll x=0; char ch=getchar();
 9     while(!isdigit(ch)) ch=getchar();
10     while(isdigit(ch)){
11         x=(x<<1)+(x<<3)+(ch^48);
12         ch=getchar();
13     }
14     return x;
15 }
16 int main(){
17     fib[1]=fib[0]=1;
18     for(int i=2;i<=65;++i) fib[i]=fib[i-1]+fib[i-2];
19     m=read();
20     while(m--){
21         a=read(); b=read();
22         if(b==a+1||a==b+1){
23             printf("1");
24             continue;
25         }
26         int x=lower_bound(fib,fib+63,a)-fib,y=lower_bound(fib,fib+63,b)-fib;
27         if(fib[x]==a&&fib[y]==b){
28             if((x-y)&1) printf("%d\n",1);
29             else printf("%lld\n",min(a,b));
30             continue;
31         }
32         while(a!=b){
33             if(b<a) a-=fib[lower_bound(fib,fib+63,a)-fib-1];
34             else b-=fib[lower_bound(fib,fib+63,b)-fib-1];
35         }
36         printf("%lld\n",a);
37     }
38     return 0;
39 }
View Code

 

 

 

B. 数颜色

刚开始把题看成了数区间内不同颜色个数,于是一直在想怎么维护pre,怎么做到$O(nlogn)$。

然后突然发现题目不一样,好像更简单了。

然后就忘了考虑vector这些东西。

打了个树状数组套主席树,

然而出题人毒瘤,查询可能比最大的权值更大。

70分死成了20分。

 

正解是vector,交换的操作直接二分后swap即可。

主席树也是可行的,交换邻项之后,后一项前缀和不变,对前一项的前缀和修改即可。

复杂度都是$O(nlogn)$。

 1 #include<iostream>
 2 #include<vector>
 3 #include<cstdio>
 4 using namespace std;
 5 const int N=300010;
 6 int a[N],n,m;
 7 vector<int> ve[N];
 8 inline int read()
 9 {
10     register int x=0; char ch=getchar();
11     while(ch<'0'||ch>'9') ch=getchar();
12     while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
13     return x;
14 }
15 int main(){
16     n=read(); m=read();
17     for(register int i=1;i<=n;++i) a[i]=read(),ve[a[i]].push_back(i);
18     for(register int i=1,opt,l,r,x;i<=m;++i){
19         opt=read();
20         if(opt==1){
21             l=read(); r=read(); x=read();
22             printf("%d\n",lower_bound(ve[x].begin(),ve[x].end(),r+1)-lower_bound(ve[x].begin(),ve[x].end(),l));
23         }
24         else{
25             x=read();
26             if(a[x]==a[x+1]) continue;
27             swap(*lower_bound(ve[a[x]].begin(),ve[a[x]].end(),x),*lower_bound(ve[a[x+1]].begin(),ve[a[x+1]].end(),x+1));
28             swap(a[x],a[x+1]);
29         }
30     }
31     return 0;
32 }
vector
 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 const int N=300010;
 5 struct node{
 6     node *lch,*rch;
 7     int val;
 8 }*root[N],pool[N*70],*ptr;
 9 int n,m,a[N],mx;
10 void insert(node register *&p,const int l,const int r,const int pos,const int val){
11     if(p) *ptr=*p;
12     p=ptr++;
13     if(l==r){
14         p->val+=val;
15         return ;
16     }
17     const int mid=l+r>>1;
18     if(pos<=mid) insert(p->lch,l,mid,pos,val);
19     else insert(p->rch,mid+1,r,pos,val);
20 }
21 inline int query(node *p,const int pos,int l=1,int r=mx){
22     while(l!=r){
23         if(!p) return 0;
24         int mid=l+r>>1;
25         if(pos<=mid) r=mid,p=p->lch;
26         else l=mid+1,p=p->rch;
27     }
28     return p?p->val:0;
29 }
30 const int L=1<<20|1;
31 char buffer[L],*S,*T;
32 #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
33 inline int read()
34 {
35     register int x=0; char ch=getchar();
36     while(ch<'0'||ch>'9') ch=getchar();
37     while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
38     return x;
39 }
40 int main(){
41     ptr=pool; n=read(); m=read();
42     for(register int i=1;i<=n;++i) a[i]=read(),mx=max(mx,a[i]);
43     for(register int i=1;i<=n;++i) root[i]=root[i-1],insert(root[i],1,mx,a[i],1);
44     for(register int i=1,opt,l,r,x;i<=m;++i){
45         opt=read();
46         if(opt==1){
47             l=read(); r=read(); x=read();
48             if(x>mx){
49                 puts("0");
50                 continue;
51             }
52             printf("%d\n",query(root[r],x)-query(root[l-1],x));
53         }
54         else{
55             x=read();
56             insert(root[x],1,mx,a[x],-1);
57             insert(root[x],1,mx,a[x+1],1);
58             swap(a[x],a[x+1]);
59         }
60     }
61     return 0;
62 }
主席树(fread卡常)

 

 

 

C. 分组

从左向右考虑,设$dp(i)$表示由i点结束,分成最少的块。

显然有$dp(i)=dp(j)+1$      $if( [j+1  ,  i]满足条件)$

至于输出方案,转移时维护pre即可。

转移的两个维度:dp值的大小,当前块是否满足条件。

显然后者是有单调性的。

也就是说,对于确定的右端点r,存在一个极坐的端点l,

l以左均不满足条件,以右均满足条件。

所以维护好 l 这个指针就好了。

 

然而这个dp是对的吗?

题目要求输出字典序最小方案。

最后一次的字典序最小,一定意味着全局最小吗?

其实已经在不知不觉中用了贪心。

从右往左不断选,选到不能选时停止,得到的一定是最优答案。

对于k=1,贪心或者dp都是可行的。

对于k=2的情况,最好使用贪心来解决。

 

对于k=1,

因为权值和不大于$512^2$,

将枚举i,j判断是否为平方数,

转化为维护一个桶表示每个数出现次数,

枚举平方数,减去i的权值,如果出现j就说明不合法。

因为只有一个分组,不能存在任何两个数的和为平方数。

 

对于k=2,

同理维护一个桶。

不妨将互相矛盾的点之间建边。

将大组分为两个小组,其实等价于图中不存在奇环。

也就是说,原图能够交叉染色,是一个二分图。

暴力染色当然不是可行的,一种可行的,维护二分图的方法是:拓展域并查集。

把每个数分为黑域和白域,当a,b加和为平方数,

如果$find(a_{white})=find(b_{white})$,即存在矛盾。

否则合并$a_{white}和b_{black},a_{black}和b_{white}$。

然而如果存在相同的数,特判将很恶心。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<vector>
 4 #include<cstring>
 5 using namespace std;
 6 const int N=132000;
 7 int n,k,a[N],mx;
 8 inline int read(){
 9     int x=0; char ch=getchar();
10     while(!isdigit(ch)) ch=getchar();
11     while(isdigit(ch)){
12         x=(x<<1)+(x<<3)+(ch^48);
13         ch=getchar();
14     }
15     return x;
16 }
17 namespace case1{
18     int cnt[N*2],dp[N],pre[N],q[N],front,tail;
19     void dfs(int x){
20         if(!x) return ;
21         dfs(pre[x]); 
22         printf("%d ",x);
23     }
24     void ooo(){
25         front=tail=1; q[1]=0;
26         for(int i=1;i<=n;++i){
27             for(int j=2;j*j<=mx*2;++j){
28                 while(front<tail&&j*j>a[i]&&cnt[j*j-a[i]]){
29                     int l=q[front]+1,r=q[++front];
30                     for(int o=l;o<=r;++o) --cnt[a[o]];
31                 }
32             }
33             dp[i]=dp[q[front]]+1; pre[i]=q[front]; 
34             while(front<=tail&&dp[i]<dp[q[tail]]) --tail;
35             q[++tail]=i; ++cnt[a[i]];
36         }
37         printf("%d\n",dp[n]);
38         dfs(pre[n]);
39         puts("");
40     }
41 }
42 namespace case2{
43     int cnt[N*2],f[N*2],stack[N],top,ans;
44     bool ban[N];
45     int find(int x){
46         return f[x]==x?x:f[x]=find(f[x]);
47     }
48     void ooo(){
49         for(int i=1;i<=mx*2;++i) f[i]=i;
50         for(int i=2;i*i<=mx*2;++i) if(i%2==0) ban[i*i/2]=1;
51         int l=n,r=n;
52         while(r){
53             while(l){
54                 bool flag=1;
55                 for(int i=2;i*i<=mx*2;++i)
56                 {
57                     if(i*i-a[l]!=a[l]) continue;
58                     if(cnt[a[l]]==1) f[find(a[l])]=find(a[l]+mx);
59                     if(cnt[a[l]]>=2) flag=0;
60                 }
61                 if(!flag) break;
62                 for(int i=2;i*i<=mx*2;++i)
63                     if(i*i-a[l]>0&&i*i-a[l]<=mx&&cnt[i*i-a[l]]&&i*i-a[l]!=a[l]){
64                         int x_w=a[l],y_w=i*i-a[l],x_b=a[l]+mx,y_b=i*i-a[l]+mx;
65                         if(cnt[i*i-a[l]]>=2&&ban[i*i-a[l]]) flag=0;
66                         if(find(x_w)==find(y_w)) flag=0;
67                         else f[find(x_w)]=find(y_b),f[find(x_b)]=f[find(y_w)];
68                     }
69                 if(!flag) break;
70                 ++cnt[a[l--]];
71             }
72             for(int i=l;i<=r;++i) cnt[a[i]]=0,f[a[i]]=a[i],f[a[i]+mx]=a[i]+mx;
73             r=l; ans++; if(r) stack[++top]=r;
74         }
75         printf("%d\n",ans);
76         while(top) printf("%d ",stack[top--]);
77         puts("");
78     }
79 }
80 int main(){
81     //freopen("x.in","r",stdin);
82     //freopen("me.out","w",stdout);
83     n=read(); k=read();
84     for(int i=1;i<=n;++i) a[i]=read(),mx=max(a[i],mx);
85     if(k==1) case1::ooo();
86     else case2::ooo();
87     return 0; 
88 }
View Code

 

posted @ 2019-08-03 20:13  skyh  阅读(250)  评论(4编辑  收藏  举报