NOI2015 题解

D1T1 程序自动分析
题目大意:给定 109 个变量和 n 个等于/不等于的关系,判断能否存在一组解满足所有关系

并查集傻逼题,NOIP小孩都会做
离散化一下,然后把相等的都用并查集并起来,判断每对不等关系是不是在同一并查集中就行了

代码没拷回来,懒得再写一遍了

D2T2 软件包管理器
题目大意:给定一棵有根树,每个点有黑白两种颜色,初始都为白色,每次进行以下两种操作:
1.将某个点所在的子树染白
2.将某个点到根路径上的点染黑
求每次操作后有多少点的颜色发生了改变

树剖傻逼题,NOIP小孩都会做
维护一棵有根树,支持链查询,链修改,子树查询,子树修改
显然链剖就行了

代码也没拷回来,懒得再写一遍了

D3T3 寿司晚宴
题目大意:给定 2...n 一共 n1 个数字,第一个人选择一些数字,第二个人选择一些数字,要求第一个人选的任意一个数字和第二个人选择的任意一个数字都互质,求方案数
n500

DP傻逼题,我竟然不会做
首先我们把一个数字看成这样一坨东西:
小于 500 的质数只有 8 个,我们开一个 28 的数组记录每个质数是否出现
大于 500 的质数只会在这个数字中出现最多一个(即一个数字中最多只有一个大于 500 的质因数),我们把数字按照这个质数分类,没有大于 500 的质因数的数每个自成一类
那么显然一类数最多只有一个人能取 两个人都取同一类数的话就会出现不互质的情况
然后我们就可以DP辣!!
fi,j 表示第一个人选择的质数集合为 i ,第二个人选择的质数集合为 j 的方案数,其中 i,j 的取值范围为 [0,28) ,二进制的第 i 位为 1 表示第 i 个质数已经取到了
gi,j,k 表示第 i+1 个人选择了这一类的数(可以不选)后第一个人选择的质数集合为 j ,第二个人选择的质数集合为 k 的方案数
枚举每一类数,首先将 f 数组复制一份给 g0 g1 ,然后这一类数中的每个数字来更新 g0 g1 ,最后用 g0+g1f 替换 f 数组即可
什么你问我为何要减掉 f ?很简单因为这一类数两个人都不选的状态在 g0 g1 中都出现了啊~
时间复杂度 O(500216)

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 550
using namespace std;
const int prime[]={2,3,5,7,11,13,17,19};
int n,p;
int f[1<<8][1<<8],g[2][1<<8][1<<8];
pair<int,int> stack[550];
int main()
{   int i,j,k;
    cin>>n>>p;
    for(i=1;i<=n;i++)
    {
        int temp=i;
        for(j=0;j<8;j++)
            if(temp%prime[j]==0)
            {
                stack[i].second|=1<<j;
                while(temp%prime[j]==0)
                    temp/=prime[j];
            }
        stack[i].first=temp;
    }
    sort(stack+2,stack+n+1);
    f[0][0]=1;
    for(i=2;i<=n;i++)
    {
        if( i==2 || stack[i].first==1 || stack[i].first!=stack[i-1].first )
        {
            memcpy(g[0],f,sizeof f);
            memcpy(g[1],f,sizeof f);
        }
        for(j=255;~j;j--)
            for(k=255;~k;k--)
            {
                if( (stack[i].second&k)==0 )
                    (g[0][j|stack[i].second][k]+=g[0][j][k])%=p;
                if( (stack[i].second&j)==0 )
                    (g[1][j][k|stack[i].second]+=g[1][j][k])%=p;
            }
        if( i==n || stack[i].first==1 || stack[i].first!=stack[i+1].first )
        {
            for(j=0;j<1<<8;j++)
                for(k=0;k<1<<8;k++)
                    f[j][k]=((g[0][j][k]+g[1][j][k]-f[j][k])%p+p)%p;
        }
    }
    int ans=0;
    for(j=0;j<1<<8;j++)
        for(k=0;k<1<<8;k++)
            if( (j&k)==0 )
                (ans+=f[j][k])%=p;
    cout<<ans<<endl;
    return 0;
}

D2T1 荷马史诗
题目大意:给定一篇文章中每个字母的出现次数,要求为每一个字母设计一个 k 进制编码使得不存在一个字母是另一个字母的前缀且总长度最小,在此基础上要求最长的字母最短

k=2 时第一问就是裸的哈夫曼树,不知道的可以想象合并果子
k-哈夫曼树就是一次合并k个,如果有零头的话就先把零头合并
最长的字母最短就是每次找高度最小的树合并
别问我怎么证明,我连合并果子都不会证…

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct abcd{
    long long size;
    int dpt;
    abcd() {}
    abcd(long long _,int __):
        size(_),dpt(__) {}
    friend bool operator < (const abcd &x,const abcd &y)
    {
        if(x.size!=y.size)
            return x.size>y.size;
        return x.dpt>y.dpt;
    }
};
int n,k,ans2;
long long ans1;
priority_queue<abcd> heap;
void Merge(int cnt)
{
    long long _size=0;
    int _dpt=0;
    while(cnt--)
    {
        abcd temp=heap.top();heap.pop();
        _size+=temp.size;
        _dpt=max(_dpt,temp.dpt);
    }
    ans1+=_size;
    ans2=max(ans2,++_dpt);
    heap.push(abcd(_size,_dpt));
}
int main()
{
    int i;long long x;
    cin>>n>>k;
    for(i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        heap.push(abcd(x,0));
    }
    if( (n-1)%(k-1)+1!=1 && n!=1 )
        Merge( min( (n-1)%(k-1)+1 , n ) );
    while(heap.size()>1)
        Merge( min( k , (int)heap.size() ) );
    cout<<ans1<<'\n'<<ans2<<endl;
    return 0;
}

D2T2 品酒大会
题目大意:给定一个长度为 n 字符串,每个后缀有一个权值 f ,对于 l=0...n1 输出 Lcp(i,j)l(i<j) 的后缀对 (i,j) 的个数以及最大的 fifj

神木韵律的超 n 级弱化版= =?
求出后缀数组后从大到小枚举 height 值,然后对于每个 height 值,两端的集合中任意一对后缀的LCP都是这个 height ,我们统计答案之后合并两端的集合,用并查集维护即可

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 300300
using namespace std;

int n;
char s[M];
long long ans1[M],ans2[M];

namespace Suffix_Array{
    int sa[M],rank[M],height[M];
    int X[M],Y[M],temp[M],tot;
    void Get_Rank()
    {
        static int sum[M];
        int i;
        for(i=1;i<=n;i++)
            sum[s[i]]++;
        for(i=1;i<=200;i++)
            sum[i]+=sum[i-1];
        for(i=n;i;i--)
            sa[sum[s[i]]--]=i;
        for(i=1;i<=n;i++)
        {
            if( i==1 || s[sa[i-1]]!=s[sa[i]] )
                ++tot;
            rank[sa[i]]=tot;
        }
    }
    void Radix_Sort(int key[],int order[])
    {
        static int sum[M];
        int i;
        for(i=0;i<=n;i++)
            sum[i]=0;
        for(i=1;i<=n;i++)
            sum[key[i]]++;
        for(i=1;i<=n;i++)
            sum[i]+=sum[i-1];
        for(i=n;i;i--)
            temp[sum[key[order[i]]]--]=order[i];
        for(i=1;i<=n;i++)
            order[i]=temp[i];
    }
    void Get_Height()
    {
        int i,j,k;
        for(i=1;i<=n;i++)
        {
            if(rank[i]==1)
                continue;
            j=sa[rank[i]-1];
            k=max(0,height[rank[i-1]]-1);
            while(s[i+k]==s[j+k])
                ++k;
            height[rank[i]]=k;
        }
    }
    void Prefix_Doubling()
    {
        int i,j;
        Get_Rank();
        for(j=1;j<=n;j<<=1)
        {
            for(i=1;i<=n;i++)
            {
                X[i]=rank[i];
                Y[i]=i+j>n?0:rank[i+j];
                sa[i]=i;
            }
            Radix_Sort(Y,sa);
            Radix_Sort(X,sa);
            for(tot=0,i=1;i<=n;i++)
            {
                if( i==1 || X[sa[i]]!=X[sa[i-1]] || Y[sa[i]]!=Y[sa[i-1]] )
                    ++tot;
                rank[sa[i]]=tot;
            }
        }
        Get_Height();
    }
}

namespace Union_Find_Set{
    int fa[M],rank[M],size[M],max_val[M],min_val[M];
    int Find(int x)
    {
        if(!fa[x])
            size[x]=1,fa[x]=x;
        if(fa[x]==x)
            return x;
        return fa[x]=Find(fa[x]);
    }
    void Union(int x,int y)
    {
        x=Find(x);y=Find(y);
        if(x==y) return ;
        if(rank[x]>rank[y])
            swap(x,y);
        if(rank[x]==rank[y])
            ++rank[y];
        fa[x]=y;size[y]+=size[x];
        max_val[y]=max(max_val[x],max_val[y]);
        min_val[y]=min(min_val[x],min_val[y]);
    }
}

bool Compare(int x,int y)
{
    using namespace Suffix_Array;
    return height[x]>height[y];
}

int main()
{
    using namespace Suffix_Array;
    using namespace Union_Find_Set;
    int i,x,y;
    cin>>n;scanf("%s",s+1);
    Prefix_Doubling();
    for(i=1;i<=n;i++)
    {
        scanf("%d",&x);
        max_val[Suffix_Array::rank[i]]=min_val[Suffix_Array::rank[i]]=x;
    }
    static int a[M];
    for(i=2;i<=n;i++)
        a[i-1]=i;
    sort(a+1,a+n,Compare);
    memset(ans2,0xef,sizeof ans2);
    for(i=1;i<n;i++)
    {
        x=Find(a[i]-1),y=Find(a[i]);
        ans1[height[a[i]]]+=(long long)size[x]*size[y];
        ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)min_val[x]*min_val[y]);
        ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)min_val[x]*max_val[y]);
        ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)max_val[x]*min_val[y]);
        ans2[height[a[i]]]=max(ans2[height[a[i]]],(long long)max_val[x]*max_val[y]);
        Union(x,y);
    }
    for(i=n-1;~i;i--)
    {
        ans1[i]+=ans1[i+1];
        ans2[i]=max(ans2[i],ans2[i+1]);
    }
    for(i=0;i<n;i++)
        printf("%lld %lld\n",ans1[i],ans1[i]==0?0:ans2[i]);
    return 0;
}

D2T3 小园丁与老司机
题目大意:给定平面上 n 个点,从原点出发,每次可以走到左、左上、上、右上、右的每个方向上第一个能到的点,一个点走过即消失,求:
1.最多能到达多少个点
2.输出任意一组方案
3.将所有方案中的左上、上、右上的路径连起来,求最小路径覆盖

DP,记录方案,建图跑最小流,这题只有细节,只有细节……

我的代码写了个费用流,被第7个点卡出翔过不了……

#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 50500
#define S (M-1)
#define T (M-2)
#define INF 0x3f3f3f3f
using namespace std;

struct Point{
    int x,y,id;
    friend istream& operator >> (istream &_,Point &p)
    {
        return scanf("%d%d",&p.x,&p.y),_;
    }
    friend bool operator < (const Point &p1,const Point &p2)
    {
        if( p1.y!=p2.y )
            return p1.y < p2.y ;
        return p1.x < p2.x ;
    }
}points[M];

int n,ans1,ans1_ed,ans2;
int f[M],g[M],from[M],_from[M];

map<int,int> m1,m2,m3;

namespace Max_Cost_Max_Flow{
    struct abcd{
        int to,flow,cost,next;
    }table[2002002];
    int head[M],tot=1;
    void Add(int x,int y,int f,int c)
    {
        table[++tot].to=y;
        table[tot].flow=f;
        table[tot].cost=c;
        table[tot].next=head[x];
        head[x]=tot;
    }
    void Link(int x,int y,int f,int c)
    {
        Add(x,y,f,c);
        Add(y,x,0,-c);
    }
    bool Edmonds_Karp()
    {
        static int q[65540],flow[M],cost[M],from[M];
        static unsigned short r,h;
        static bool v[M];
        int i;
        memset(cost,0xef,sizeof cost);
        flow[S]=INF;cost[S]=0;q[++r]=S;
        while(r!=h)
        {
            int x=q[++h];v[x]=false;
            for(i=head[x];i;i=table[i].next)
                if(table[i].flow&&cost[table[i].to]<cost[x]+table[i].cost)
                {
                    cost[table[i].to]=cost[x]+table[i].cost;
                    flow[table[i].to]=min(flow[x],table[i].flow);
                    from[table[i].to]=i;
                    if(!v[table[i].to])
                        v[table[i].to]=true,q[++r]=table[i].to;
                }
        }
        if(cost[T]<=0) return false;
        ans2+=flow[T];
        for(i=from[T];i;i=from[table[i^1].to])
            table[i].flow-=flow[T],table[i^1].flow+=flow[T];
        return true;
    }
}

int main()
{
    using namespace Max_Cost_Max_Flow;
    int i,j;
    cin>>n;
    for(i=1;i<=n;i++)
        cin>>points[i],points[i].id=i;
    sort(points+1,points+n+1);
    static int stack[M],top;
    memset(f,0xef,sizeof f);f[0]=0;
    for(i=0;i<=n;i++)
    {
        if(m1.find(points[i].x)!=m1.end())
        {
            int temp=m1[points[i].x];
            if(f[temp]+1>f[i]) f[i]=f[temp]+1,from[i]=temp;
        }
        m1[points[i].x]=i;

        if(m2.find(points[i].x+points[i].y)!=m2.end())
        {
            int temp=m2[points[i].x+points[i].y];
            if(f[temp]+1>f[i]) f[i]=f[temp]+1,from[i]=temp;
        }
        m2[points[i].x+points[i].y]=i;

        if(m3.find(points[i].x-points[i].y)!=m3.end())
        {
            int temp=m3[points[i].x-points[i].y];
            if(f[temp]+1>f[i]) f[i]=f[temp]+1,from[i]=temp;
        }
        m3[points[i].x-points[i].y]=i;

        stack[++top]=i;
        if( i==n || points[i+1].y!=points[i].y )
        {
            static int _f[M];
            for(j=1;j<=top;j++)
            {
                _f[stack[j]]=f[stack[j]];
                _from[stack[j]]=from[stack[j]];
            }
            int max_pos=-1;
            for(j=2;j<=top;j++)
            {
                if( max_pos==-1 || _f[stack[j-1]]>_f[stack[max_pos]] )
                    max_pos=j-1;
                if( _f[stack[max_pos]]+(j-1)>f[stack[j]] )
                    f[stack[j]]=_f[stack[max_pos]]+(j-1),from[stack[j]]=stack[max_pos];
            }
            max_pos=-1;
            for(j=top-1;j;j--)
            {
                if( max_pos==-1 || _f[stack[j+1]]>_f[stack[max_pos]] )
                    max_pos=j+1;
                if( _f[stack[max_pos]]+(top-j)>f[stack[j]] )
                    f[stack[j]]=_f[stack[max_pos]]+(top-j),from[stack[j]]=stack[max_pos];
            }

            for(j=1;j<=top;j++)
                if(f[stack[j]]>ans1)
                    ans1=f[stack[j]],ans1_ed=stack[j];

            top=0;
        }
    }

    cout<<ans1<<endl;
    bool flag=false;
    while(ans1_ed)
    {
        stack[++top]=points[ans1_ed].id;
        if(flag)
        {
            flag=false;
            ans1_ed=_from[ans1_ed];
            continue;
        }
        if(points[ans1_ed].y==points[from[ans1_ed]].y)
        {
            if(ans1_ed>from[ans1_ed])
            {
                for(i=ans1_ed-1;i!=from[ans1_ed];i--)
                    stack[++top]=points[i].id;
                for(i--;points[i].y==points[ans1_ed].y;i--);
                for(i++;i!=from[ans1_ed];i++)
                    stack[++top]=points[i].id;
            }
            else
            {
                for(i=ans1_ed+1;i!=from[ans1_ed];i++)
                    stack[++top]=points[i].id;
                for(i++;points[i].y==points[ans1_ed].y;i++);
                for(i--;i!=from[ans1_ed];i--)
                    stack[++top]=points[i].id;
            }
            flag=true;
        }
        ans1_ed=from[ans1_ed];
    }
    //cout<<top<<endl;
    while(top)
        printf("%d ",stack[top--]);
    puts("");

    m1.clear();
    m2.clear();
    m3.clear();

    memcpy(g,f,sizeof g);

    for(i=n;~i;i--)
    {
        f[i]=(i!=0);
        if(m1.find(points[i].x)!=m1.end())
        {
            int temp=m1[points[i].x];
            f[i]=max(f[temp]+(i!=0),f[i]);
            //printf("%d\n",g[i]+f[temp]);
            if(g[i]+f[temp]==ans1)
            {
                Link(temp,i,1,1);
                Link(temp,i,INF,0);
            }
        }
        m1[points[i].x]=i;

        if(m2.find(points[i].x+points[i].y)!=m2.end())
        {
            int temp=m2[points[i].x+points[i].y];
            f[i]=max(f[temp]+(i!=0),f[i]);
            //printf("%d\n",g[i]+f[temp]);
            if(g[i]+f[temp]==ans1)
            {
                Link(temp,i,1,1);
                Link(temp,i,INF,0);
            }
        }
        m2[points[i].x+points[i].y]=i;

        if(m3.find(points[i].x-points[i].y)!=m3.end())
        {
            int temp=m3[points[i].x-points[i].y];
            f[i]=max(f[temp]+(i!=0),f[i]);
            //printf("%d\n",g[i]+f[temp]);
            if(g[i]+f[temp]==ans1)
            {
                Link(temp,i,1,1);
                Link(temp,i,INF,0);
            }
        }
        m3[points[i].x-points[i].y]=i;

        stack[++top]=i;
        if( i==0 || points[i-1].y!=points[i].y )
        {
            static int _f[M];
            for(j=1;j<=top;j++)
                _f[stack[j]]=f[stack[j]];
            int max_pos=-1;
            for(j=2;j<=top;j++)
            {
                if( max_pos==-1 || _f[stack[j-1]]+(top-j+1)>_f[stack[max_pos]]+(top-max_pos) )
                    max_pos=j-1;
                if( _f[stack[max_pos]]+(top-max_pos)>f[stack[j]] )
                    f[stack[j]]=_f[stack[max_pos]]+(top-max_pos);
            }
            max_pos=-1;
            for(j=top-1;j;j--)
            {
                if( max_pos==-1 || _f[stack[j+1]]+(j)>_f[stack[max_pos]]+(max_pos-1) )
                    max_pos=j+1;
                if( _f[stack[max_pos]]+(max_pos-1)>f[stack[j]] )
                    f[stack[j]]=_f[stack[max_pos]]+(max_pos-1);
            }
            top=0;
        }
    }

    m1.clear();
    m2.clear();
    m3.clear();
    /* for(i=0;i<=n;i++) printf("%d\n",f[i]+g[i]); for(i=0;i<=n;i++) { if(m1.find(points[i].x)!=m1.end()) { int temp=m1[points[i].x]; if(g[temp]+f[i]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m1[points[i].x]=i; if(m2.find(points[i].x+points[i].y)!=m2.end()) { int temp=m2[points[i].x+points[i].y]; if(g[temp]+f[i]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m2[points[i].x+points[i].y]=i; if(m3.find(points[i].x-points[i].y)!=m3.end()) { int temp=m3[points[i].x-points[i].y]; if(g[temp]+f[i]==ans1) { Link(temp,i,1,1); Link(temp,i,INF,0); } } m3[points[i].x-points[i].y]=i; } */
    for(i=0;i<=n;i++)
    {
        Link(S,i,INF,0);
        Link(i,T,INF,0);
    }
    while( Edmonds_Karp() );
    cout<<ans2<<endl;
    return 0;
}

你可能感兴趣的:(NOI,NOI2015)