2019-8-3 考试总结

A. 斐波那契

很水,但是没看出性质,

它的部分分可以拿到$70+$,所以就水到了$80$分。

很好找的一个规律,

每个节点的编号减去上一个$fibonacci$数就是它的父节点的编号。

所以每次上翻就可以了。

说起来很简单,细节很重要。

丑陋的代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#define int long long
#define Maxn 300050
#define Reg register
#define abs(x) ((x)<0?(-1*(x)):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int m,num[Maxn],sup[Maxn],LCA;
int getf(int x,int y)
{
    if(x<y) swap(x,y);
    while(x>y)
    {
        int l1=lower_bound(num+1,num+59+1,x)-num;
        x=x-num[l1-1];
        if(x<y) swap(x,y);
    }
    return x;
}
signed main()
{
    scanf("%lld",&m);
    num[1]=1,num[2]=2;
    for(Reg int i=3;i<=59;++i) num[i]=num[i-1]+num[i-2];
    for(Reg int i=1,x,y;i<=m;++i)
    {
        scanf("%lld%lld",&x,&y);
        LCA=getf(x,y);
        printf("%lld\n",LCA);
    }
    return 0;
}
View Code

B.数颜色

题解的一句话说得好:高级数据结构学傻了。

什么莫队,什么主席树,线段树。。

只要用一个$vector$就可以了。

用$vector$存颜色的位置。

每次二分查找。

修改的时候因为只是$x$和$x+1$交换,对$vector$里的元素顺序没有改变。

所以用不着考虑排序了。

复杂度$O(nlogn)$。

丑陋的代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#define int long long
#define Maxn 300050
#define Reg register
using namespace std;
int n,m,ans,A[Maxn];
vector<vector<int> > pos(Maxn);
signed main()
{
    scanf("%lld%lld",&n,&m);
    for(Reg int i=1;i<=n;++i)
    {
        scanf("%lld",&A[i]);
        pos[A[i]].push_back(i);
    }
    for(Reg int i=1,l,r,opt,c;i<=m;++i)
    {
        scanf("%lld",&opt);
        if(opt==1)
        {
            scanf("%lld%lld%lld",&l,&r,&c);
            int p1=upper_bound(pos[c].begin(),pos[c].end(),r)-pos[c].begin()-1;
            int p2=lower_bound(pos[c].begin(),pos[c].end(),l)-pos[c].begin();
            printf("%lld\n",p1-p2+1);
        }
        else
        {
            scanf("%lld",&c);
            if(c==n||A[c]==A[c+1]) continue;
            int p1=lower_bound(pos[A[c]].begin(),pos[A[c]].end(),c)-pos[A[c]].begin();
            int p2=lower_bound(pos[A[c+1]].begin(),pos[A[c+1]].end(),c+1)-pos[A[c+1]].begin();
            pos[A[c]][p1]=c+1,pos[A[c+1]][p2]=c;
            swap(A[c],A[c+1]);
        }
    }
    return 0;
}
View Code

C. 分组

考试时打了一个$dp$,水到$32$分,

正解:

当$K=1$时,

要找能够拓展的最长长度。

因为要记录前趋,所以可以从后向前枚举。

每次判断能不能拓展到它,如果可以,就拓展。

否则要把这个点设为断点。

但是,显然这个是$O(n^2)$的。

因为题目性质,能产生矛盾的都是平方数,所以可以枚举平方因子$x$,

判断每个$x^2-a[i]$是不是在当前的队列中,如果存在,那么肯定不能拓展。

复杂度降为$O(n\sqrt n)$。

当$K=2$时,

首先,如果$n=2$,那么直接输出$1$和$2$个回车,$4$分拿到。

之后就。。

二分图。

将能产生矛盾的兔子建边,判断整张图是不是二分图,如果是就可以扩展,否则为断点。

然后卡一卡就$O(n^2)$过了。

但是真正的正解要用并查集。

丑陋的代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#define int long long
#define Maxn 150000
#define Reg register
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,K,tot,root,top,maxx,A[Maxn],vis[Maxn*2],stack[Maxn],fir[Maxn],col[Maxn],sta[Maxn];
struct Tu {int st,ed,next;} lian[Maxn*2];
vector<vector<int> >son(Maxn*2);
void add(int x,int y)
{
    if(!fir[x]) sta[++sta[0]]=x;
    lian[++top].st=x;
    lian[top].ed=y;
    lian[top].next=fir[x];
    fir[x]=top;
    return;
}
bool dfs(int x,int fa,int co)
{
    if(x>stack[tot]) return 1;
    col[x]=co;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(lian[i].ed==fa||lian[i].ed==x) continue;
        if(col[lian[i].ed]&&lian[i].ed<=stack[tot])
        {
            if(col[lian[i].ed]==col[x]) return 0;
            else continue;
        }
        else if(!dfs(lian[i].ed,x,3-co)) return 0;
    }
    return 1;
}
bool judge(int x)
{
    for(Reg int i=1;i<=sta[0];++i) col[sta[i]]=0;
    for(Reg int i=1;i<=sta[0];++i)
    {
        if(col[sta[i]]||!fir[sta[i]]) continue;
        if(!dfs(sta[i],sta[i],1)) return 0;
    }
    return 1;
}
signed main()
{
//    freopen("division21.in","r",stdin);
    scanf("%lld%lld",&n,&K);
    for(Reg int i=1;i<=n;++i)
    {
        scanf("%lld",&A[i]);
        maxx=max(maxx,A[i]);
    }
    if(K==1)
    {
        maxx=sqrt(maxx<<1)+1;
        stack[++tot]=n+1;
        for(Reg int i=n;i>=1;--i)
        {
            int ok=1;
            for(Reg int j=maxx;j*j>=A[i];--j)
                if(vis[j*j-A[i]]) {ok=0; break;}
            if(!ok)
            {
                for(Reg int j=stack[tot];j>=i;--j) vis[A[j]]=0;
                stack[++tot]=i;
            }
            vis[A[i]]=1;
        }
        printf("%lld\n",tot);
        for(Reg int i=tot;i>=1;--i)
            if(stack[i]<n) printf("%lld ",stack[i]);
    }
    else
    {
        if(n==2) printf("1\n\n");
        else
        {
            stack[++tot]=n;
            son[A[n]].push_back(n);
            maxx=sqrt(maxx<<1)+1;
            for(Reg int i=n-1;i>=1;--i)
            {
                int ok=1;
                for(Reg int j=maxx;j*j>=A[i];--j)
                {
                    if(son[j*j-A[i]].size()==0) continue;
                    else for(Reg int k=0;k<son[j*j-A[i]].size();++k)
                        add(i,son[j*j-A[i]][k]),add(son[j*j-A[i]][k],i);
                }
                if(!judge(i))
                {
                    for(Reg int j=1;j<=sta[0];++j) fir[sta[j]]=0;
                    for(Reg int j=stack[tot];j>=i;--j) son[A[j]].clear();
                    sta[0]=0;
                    top=0; stack[++tot]=i;
                }
                son[A[i]].push_back(i);
            }
            int ans=0;
            printf("%lld\n",tot);
            for(Reg int i=tot;i>=1;--i)
                if(stack[i]<n) printf("%lld ",stack[i]);
        }
    }
    return 0;
}
View Code

总结:

一开始看$T1$,感觉没什么思路,没找规律,就跳过了。

然后开始看$T2$,一看到题目绝对是个数据结构题。

想打动态开点线段树,看到数据范围$3\times 10^5$,然后就咕咕咕。

这套题测试点写的很清楚。

于是,测试点分治很好用。

前30分暴力树状数组,后面根据特殊性质再用别的。

于是水到$55$分。

之后回来看$T1$,打了一个$dfs$,开始找规律。

最后其实还是水到的$80$分。

$T3$有一些送分点,庆幸都拿到了。。

要不又掉下去了。

所以最后$80+55+32=167$。

没什么水平。。。

posted @ 2019-08-03 16:42  Milk_Feng  阅读(140)  评论(1编辑  收藏  举报