模拟12题解

T1[A. 斐波那契(fibonacci)]

考场上发现了一个性质:一个节点的父节点=该节点编号-上一个fibonacci数

所以求父亲节点代码

int fa(int x)
{
    for(int i=mx;i;i--)
        if(fi[i]<x)
            return x-fi[i];
}

然后我只想到了建树的70%代码,数组越界了又少了30分QAQ

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #define int long long
 7 using namespace std;
 8 inline int read()
 9 {
10     int f=1,x=0;char ch=getchar();
11     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
13     return f*x;
14 }
15 const int mx=65;
16 const int maxn=1000000;
17 int fi[mx+10];
18 int d[maxn+5];
19 int f[maxn+5][25];
20 struct node{
21     int v,nxt;
22 }e[maxn];int h[maxn],nu;
23 void add(int x,int y)
24 {
25     e[++nu].v=y;
26     e[nu].nxt=h[x];
27     h[x]=nu;
28 }
29 int gfa(int x)
30 {
31     for(int i=mx;i;i--)
32         if(fi[i]<x)
33             return x-fi[i];
34 }
35 void bfs()
36 {
37     queue<int>q;
38     d[1]=1;
39     q.push(1);
40     while(q.size())
41     {
42         int x=q.front();q.pop();
43         for(int i=h[x];i;i=e[i].nxt)
44         {
45             int y=e[i].v;
46             if(d[y])continue;
47             d[y]=d[x]+1;
48             f[y][0]=x;
49             for(int j=1;j<=21;j++)
50                 f[y][j]=f[f[y][j-1]][j-1];
51             q.push(y);
52         }
53     }
54 }
55 int lca(int x,int y)
56 {
57     if(d[x]>d[y])swap(x,y);
58     for(int i=21;i>=0;i--)
59         if(d[f[y][i]]>=d[x])
60             y=f[y][i];
61     if(y==x)return x;
62     for(int i=21;i>=0;i--)
63         if(f[x][i]!=f[y][i])
64             x=f[x][i],y=f[y][i];
65     return f[x][0];    
66 }
67 signed main()
68 {
69     //freopen("fibonacci2.in","r",stdin);
70     int m=read(),mxx;
71     fi[0]=fi[1]=1;
72     for(int i=2;i<=mx;i++)
73         fi[i]=fi[i-1]+fi[i-2];
74     for(int i=2;i<=maxn;i++)
75     {
76         int fa=gfa(i);
77         add(fa,i);
78     }
79     bfs();
80     for(int i=1;i<=m;i++)
81     {
82         int x=read(),y=read();
83         printf("%lld\n",lca(x,y));
84     }
85 }
86 /*
87 g++ 1.cpp -o 1
88 ./1
89 
90 */
View Code

可是考完才发现暴力既好写,又能A

对于输入的两个节点x,y将其中一个向上遍历,并把经过的路径都存入一个vector

然后另一个向上翻,如果在vector中能找到,那此时的y就是答案

时间复杂度$O(60mlog(n))$

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<vector>
 7 #define int long long
 8 using namespace std;
 9 inline int read()
10 {
11     int f=1,x=0;char ch=getchar();
12     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
14     return f*x;
15 }
16 const int mx=65;
17 int fi[mx+10];
18 int fa(int x)
19 {
20     for(int i=mx;i;i--)
21         if(fi[i]<x)
22             return x-fi[i];
23 }
24 signed main()
25 {
26     //freopen("data","r",stdin);
27     int m=read();
28     fi[0]=fi[1]=1;
29     for(int i=2;i<=mx;i++)
30         fi[i]=fi[i-1]+fi[i-2];
31     vector<int>v;
32     for(int i=1;i<=m;i++)
33     {
34         int x=read(),y=read();
35         v.clear();
36         while(x>1)
37         {
38             v.push_back(x);
39             x=fa(x);
40         }
41         while(y>1)
42         {
43             vector<int>::iterator res=find(v.begin(),v.end(),y);
44             if(res!=v.end())  break;
45             y=fa(y);
46         }
47         printf("%lld\n",y);
48     }
49 }
50 /*
51 g++ 1.cpp -o 1
52 ./1
53 
54 */
View Code

vector中查找元素:

vector<int>::iterator res=find(v.begin(),v.end(),y);//在v中查找y
if(res!=v.end()) //找到了 

 

T2[B. 数颜色]「排序」「线段树」「分块」「莫队」

考场上只想到了$O(nm)$的纯暴力,

然而以上的这些都能拿到不少的部分分,更有主席树,树套树的高超方法,我却什么都搞不出来

65%+算法:动态开点线段树

考试时想用线段树,当时的思维是这样的:死板地维护区间1-n,然后查询。。。打到一半,诶,查询什么?线段树里开3e5个颜色的个数,不可能的。。。

然后就傻了,想想其他的数据结构我也没法维护。。

殊不知,,我"忘"了一个叫权值线段树的东西,动态开点,每棵树维护每种color的在原区间的个数,时空复杂度$O(nlog(n))$

使用动动的代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m;
 4 int a[300001];
 5 int rt[300001],trsum[10000000],son[10000000][2],tot;
 6 void change(int &x,int l,int r,int to,int d)
 7 {
 8     if(!x)x=++tot;
 9     trsum[x]+=d;
10     if(l==r)return;
11     int mid=(l+r)>>1;
12     if(to<=mid)change(son[x][0],l,mid,to,d);
13     else change(son[x][1],mid+1,r,to,d);
14 }
15 int ask(int x,int l,int r,int L,int R)
16 {
17     if(!x)return 0;
18     if(l==L&&r==R)return trsum[x];
19     int mid=(l+r)>>1;
20     if(L>mid)return ask(son[x][1],mid+1,r,L,R);
21     else if(R<=mid)return ask(son[x][0],l,mid,L,R);
22     else return ask(son[x][0],l,mid,L,mid)+ask(son[x][1],mid+1,r,mid+1,R);
23 }
24 int main()
25 {
26     scanf("%d%d",&n,&m);
27     for(int i=1;i<=n;i++)
28     {
29         scanf("%d",&a[i]);
30         change(rt[a[i]],1,n,i,1);
31     }
32     while(m--)
33     {
34         int opt;
35         scanf("%d",&opt);
36         if(opt==1)
37         {
38             int l,r,c;
39             scanf("%d%d%d",&l,&r,&c);
40             printf("%d\n",ask(rt[c],1,n,l,r));
41         }
42         else
43         {
44             int x;
45             scanf("%d",&x);
46             change(rt[a[x  ]],1,n,x  ,-1);
47             change(rt[a[x  ]],1,n,x+1, 1);
48             change(rt[a[x+1]],1,n,x+1,-1);
49             change(rt[a[x+1]],1,n,x  , 1);
50             a[x]^=a[x+1]^=a[x]^=a[x+1];
51         }
52     }
53     return 0;
54 }
View Code

65%+算法:分块

我现在已经想不起来我当时怎么把分块给否了的,可能当时粗略一想空间不够,但是分完块就没几块了,复杂度$O(m\sqrt{n})$这么直接的算法我也想不到,想得严密一些不要轻易就放弃自己的想法

正解:

将(颜色,位置)按照二元组排序,对于查询操作,使用结构体lower_bound二分查找

对于修改操作,暴力修改结构体的pos,

注意若两个颜色不同,那么修改不会改变每个颜色块里的顺序

若相同,不用修改

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define mid ((l+r)>>1)
 6 using namespace std;
 7 const int maxn=3e5+5;
 8 inline int read()
 9 {
10     int f=1,x=0;char ch=getchar();
11     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
13     return f*x;
14 }
15 int n,sum[maxn],jl[maxn]; 
16 struct node{
17     int pos,da;
18 }a[maxn];
19 bool cmp(node x,node y){return (x.da==y.da)?x.pos<y.pos:x.da<y.da;}
20 int main()
21 {
22 //    freopen("data","r",stdin);
23     n=read();int Q=read(),mc=0;
24     for(int i=1;i<=n;i++)
25     {
26         a[i].da=read(),a[i].pos=i;
27         sum[a[i].da]++;
28         mc=max(mc,a[i].da);
29         jl[i]=a[i].da;
30     }
31     for(int i=1;i<=mc;i++)
32         sum[i]+=sum[i-1];
33     sort(a+1,a+n+1,cmp);
34     while(Q--)
35     {
36         int opt=read();
37         if(opt==1)
38         {
39             int L=read(),R=read(),col=read();
40             node l,r;
41             l.da=col,l.pos=L;
42             r.da=col,r.pos=R;
43             int ll=lower_bound(a+sum[col-1]+1,a+sum[col]+1,l,cmp)-a;
44             int rr=upper_bound(a+sum[col-1]+1,a+sum[col]+1,r,cmp)-a;
45             printf("%d\n",rr-ll);
46         }
47         if(opt==2)
48         {
49             int i=read();
50             int x=i,y=i+1;
51             if(jl[x]==jl[y])continue;
52             node xx,yy;
53             xx.pos=x,xx.da=jl[x];int c1=jl[x];
54             yy.pos=y,yy.da=jl[y];int c2=jl[y];
55             int xp=lower_bound(a+sum[c1-1]+1,a+sum[c1]+1,xx,cmp)-a;
56             int yp=lower_bound(a+sum[c2-1]+1,a+sum[c2]+1,yy,cmp)-a;
57             a[xp].pos=y,a[yp].pos=x;
58             jl[x]=yy.da,jl[y]=xx.da;
59         }
60     }
61 }
62 /*
63 g++ 2.cpp -o 2
64 ./2
65 
66 */
View Code

结构体lower_bound:

bool cmp(node x,node y){return (x.da==y.da)?x.pos<y.pos:x.da<y.da;}
int ll=lower_bound(a+1,a+n+1,l,cmp)-a;//a中查找l返回下标ll

 

T3[C. 分组]「二分图」

性质:若要保证字典序最小,则从后往前枚举,让每一段区间尽量长,那么可以保证字典序最小

k==1时,从后往前枚举,看枚举到的点与已有的点有无矛盾,若有则该点为断点

k==2时,同样的枚举方式对于枚举到的点的判断方式:

每段区间就是一个二分图,

1:若三者互相矛盾 ,那么当枚举到第三个时,发现第三个与其中一个放不到两边,设置为断点

2:若有两个相同的颜色,第三个不同颜色,枚举到的第三个是不同颜色时,或是两个相同颜色中的一个,都要干掉

实现见代码注释

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<vector>
 6 #include<cmath>
 7 using namespace std;
 8 inline int read()
 9 {
10     int f=1,x=0;char ch=getchar();
11     while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
13     return f*x;
14 }
15 const int maxn=140000;
16 int mx,n,k,a[maxn+100],issqr[2*maxn+100],cnt,ans[maxn+100],v[maxn+100],fa[2*maxn+100];
17 int find(int x)
18 {
19     if(fa[x]!=x)fa[x]=find(fa[x]);
20     return fa[x];
21 }
22 int jud(int x,int y)//x y have had conflict  &&判断是否能在两边
23 {
24     int x1=find(x),x2=find(x+maxn);//x1 x的敌人代表元素  x2  朋友
25     int y1=find(y),y2=find(y+maxn);
26     if(x1==y1||x2==y2)return 0;//have same friend or emery
27     fa[x1]=y2,fa[x2]=y1;// divide into two different jihe
28     return 1;    
29 }
30 void solve1()
31 {
32     for(int i=1;i<=n;i++)mx=max(mx,a[i]);
33     ans[cnt]=n;
34     for(int i=n;i>=1;i--)
35     {
36         for(int j=sqrt(a[i]);j*j<=mx+a[i];j++)
37         {
38             if(j*j-a[i]>0&&v[j*j-a[i]])
39             {
40                 for(int k=i+1;k<=ans[cnt];k++)v[a[k]]=0;
41                 ans[++cnt]=i;
42                 break;
43             }
44        }
45        v[a[i]]=1;
46     }
47 }
48 int vis[maxn],svis[maxn];
49 void solve2()
50 {
51     for(int i=1;i<=2*maxn;i++)fa[i]=i;
52     for(int i=1;i*i<=2*maxn;i++) issqr[i*i]=1;
53     for(int i=n,j=n;i>=1;)
54     {
55         for(;j>=1;j--)
56         {
57             if(vis[a[j]])//如果这个点出现过一次
58             {
59                 if(!issqr[2*a[j]])continue;//并且这个点与自己同颜色的矛盾
60                 if(svis[a[j]])goto fail;//如果这个点已经出现了两次,两个可以分到二分图的两边,所以不可能就再有了,成为断点
61                 for(int k=1;k*k<=mx+a[j];k++)//所以这是第二个点
62                     if(k*k-a[j]>0&&vis[k*k-a[j]]&&k*k!=2*a[j])goto fail;//找到了另一个与该颜色矛盾,跳过成为断点
63                 svis[a[j]]=1;//这种颜色出现了两次,并且在二分图两边(1)
64             }
65             else //没有出现过
66             {
67                 for(int k=1;k*k<=mx+a[j];k++)
68                     if(k*k-a[j]>0&&vis[k*k-a[j]])//找到了与它矛盾的点
69                         if(svis[k*k-a[j]]||jud(a[j],k*k-a[j])==0)//如果这个点已经在二分图的两边(1情况)  或 这两个点不能放在两边
70                             {fa[a[j]]=a[j];fa[a[j]+maxn]=a[j]+maxn;goto fail;}//清空这个断点的并查集信息
71                 vis[a[j]]=1;
72             }
73         }
74         fail:
75         if(j==0)break;
76         ans[++cnt]=j;
77         for(;i>j;i--)fa[a[i]]=a[i],fa[a[i]+maxn]=a[i]+maxn,vis[a[i]]=svis[a[i]]=0;
78     }
79 }
80 int main()
81 {
82     n=read(),k=read();
83     for(int i=1;i<=n;i++)
84         a[i]=read(),mx=max(mx,a[i]);
85     if(k==1)solve1();
86     else solve2();
87     printf("%d\n",cnt+1);
88     for(int i=cnt;i>=1;i--) printf("%d ",ans[i]);
89     puts("");
90 }
91 /*
92 g++ 1.cpp -o 1
93 ./1
94 
95 */
View Code
posted @ 2019-08-03 17:59  casun547  阅读(132)  评论(0编辑  收藏  举报