NOI2015 迟来的测试,及时的总结

因为耽误了网络同步赛,所以在将近一个月后进行了NOI的测试。

DAY1

T1程序自动分析

题目大意:给定一些变量相等或不等的关系,判断是否矛盾。

思路:离散化后,并查集维护一下。水水的开始。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxnode 200005
using namespace std;
struct use{
    int x1,x2,kk;
}ask[maxnode]={0};
int ai[maxnode]={0},fa[maxnode]={0};
int cmp(const use &x,const use &y){return x.kk>y.kk;}
int root(int x)
{
    if (fa[x]!=x) fa[x]=root(fa[x]);
    return fa[x];
}
int main()
{
    freopen("prog.in","r",stdin);
    freopen("prog.out","w",stdout);
    
    int t,i,j,n,m,siz,r1,r2;
    bool f;
    scanf("%d",&t);
    while(t)
    {
        scanf("%d",&n);ai[0]=0;
        for (i=1;i<=n;++i)
        {
            scanf("%d%d%d",&ask[i].x1,&ask[i].x2,&ask[i].kk);
            ai[++ai[0]]=ask[i].x1;
            ai[++ai[0]]=ask[i].x2;
        }
        sort(ai+1,ai+ai[0]+1);f=false;
        siz=unique(ai+1,ai+ai[0]+1)-ai-1;
        sort(ask+1,ask+n+1,cmp);
        for (i=1;i<=2*n;++i) fa[i]=i;
        for (i=1;i<=n;++i)
        {
            ask[i].x1=upper_bound(ai+1,ai+siz+1,ask[i].x1)-ai-1;
            ask[i].x2=upper_bound(ai+1,ai+siz+1,ask[i].x2)-ai-1;
            r1=root(ask[i].x1);r2=root(ask[i].x2);
            if (ask[i].kk==1)
            {
                if (r1!=r2) 
                {
                   if (i%3==0) swap(r1,r2);
                   fa[r1]=r2;
                }
            }
            else
            {
                if (r1==r2) 
                {
                   f=true;break;
                }
            }
        }
        if (f) printf("NO\n");
        else printf("YES\n");
        --t;
    }
    
    fclose(stdin);
    fclose(stdout);
}
View Code

T2软件包管理器

题目大意:给定一些软件之间的依赖关系(一棵树结构),安装时要把它到根上的都安装,卸载时要把它子树里的都卸载,求每一个操作改变的软件个数。

思路:树链剖分——链和子树,线段树维护。水水的延续。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxnode 100005
using namespace std;
char ch[10];
int tot=0,point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},t[maxnode*4]={0},
    ri[maxnode]={0},delta[maxnode*4];
bool visit[maxnode]={false};
void add(int u,int v)
{
    ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;
    ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;
}
void updata(int i){t[i]=t[i*2]+t[i*2+1];}
void pushdown(int i,int l,int r)
{
    int mid;
    mid=(l+r)/2;
    if (delta[i]>=0)
    {
        delta[i*2]=delta[i];
        t[i*2]=(delta[i*2]==1 ? (mid-l+1) : 0);
        delta[i*2+1]=delta[i];
        t[i*2+1]=(delta[i*2+1]==1 ? (r-mid) : 0);
        delta[i]=delta[0];
    }
}
int task(int i,int l,int r,int ll,int rr,int kk)
{
    int mid,ans=0;
    if (ll<=l&&r<=rr) return (kk==1 ? t[i] : r-l+1-t[i]);
    pushdown(i,l,r);mid=(l+r)/2;
    if (ll<=mid) ans+=task(i*2,l,mid,ll,rr,kk);
    if (rr>mid) ans+=task(i*2+1,mid+1,r,ll,rr,kk);
    return ans;
}
void tch(int i,int l,int r,int ll,int rr,int kk)
{
    int mid;
    if (ll<=l&&r<=rr)
    {
        delta[i]=kk;t[i]=(kk==1 ? (r-l+1) : 0);return;
    }
    mid=(l+r)/2;pushdown(i,l,r);
    if (ll<=mid) tch(i*2,l,mid,ll,rr,kk);
    if (rr>mid) tch(i*2+1,mid+1,r,ll,rr,kk);
    updata(i);
}
struct lp{
    int fa[maxnode],dep[maxnode],son[maxnode],siz[maxnode],tid[maxnode],top[maxnode];
    void dfs1(int u,int f,int depth)
    {
        int i,j,maxsiz=0;
        visit[u]=true;fa[u]=f;dep[u]=depth;
        siz[u]=1;son[u]=0;
        for (i=point[u];i;i=next[i])
        {
            if (!visit[j=en[i]])
            {
                dfs1(j,u,depth+1);
                siz[u]+=siz[j];
                if (siz[j]>maxsiz)
                {
                    maxsiz=siz[j];
                    son[u]=j;
                }
            }
        }
    }
    void dfs2(int u,int anc)
    {
        int i,j;
        visit[u]=false;tid[u]=++tot;top[u]=anc;
        if (son[u]) dfs2(son[u],anc);
        for (i=point[u];i;i=next[i])
            if (visit[j=en[i]]) dfs2(j,j);
        ri[u]=tot;
    }
    int ins(int a,int b)
    {
        int ans=0;
        while(top[a]!=top[b])
        {
            if (dep[top[a]]<dep[top[b]]) swap(a,b);
            ans+=task(1,1,tot,tid[top[a]],tid[a],0);
            tch(1,1,tot,tid[top[a]],tid[a],1);
            a=fa[top[a]];
        }
        if (dep[a]>dep[b]) swap(a,b);
        ans+=task(1,1,tot,tid[a],tid[b],0);
        tch(1,1,tot,tid[a],tid[b],1);
        return ans;
    }
    int uni(int a)
    {
        int ans=0;
        ans=task(1,1,tot,tid[a],ri[a],1);
        tch(1,1,tot,tid[a],ri[a],0);
        return ans;
    }
}tree;
int main()
{
    freopen("manager.in","r",stdin);
    freopen("manager.out","w",stdout);
    
    int n,q,i,j;
    scanf("%d",&n);
    for (i=2;i<=n;++i)
    {
        scanf("%d",&j);add(++j,i);
    }tot=0;
    tree.dfs1(1,0,1);tree.dfs2(1,1);
    memset(delta,128,sizeof(delta));
    scanf("%d",&q);
    for (i=1;i<=q;++i)
    {
        scanf("%*c%s%d",&ch,&j);
        if (ch[0]=='i')
        {
            printf("%d\n",tree.ins(1,++j));
        }
        else
        {
            printf("%d\n",tree.uni(++j));
        }
    }
    
    fclose(stdin);
    fclose(stdout);
}
View Code

T3寿司晚宴

题目大意:给定n-1种寿司,每个寿司编号2~n,求两个人去寿司的和谐方案的方案总数(一个方案称为和谐的当且仅当两人选择的寿司种类中两两编号互质。)

思路:测试的时候,只会暴力打表,但是还因为常数赋值时没写LL就挂了。后来问了sunshine大爷才知道正解。首先我们知道一个数n最多只有一个大于根号n的质因子,所以我们可以对一个数构成两个特征值:第1个表示大于根n的质因子,第2个表示小于根n的质因数组成(用8位2进制数表示,根号500以内的质数只有8个)。对第一个特征值排序后对这个大质因数相同的一类数一起处理(如果这个特征值为1,则一个一个的处理)。设f[i][j]表示第一个人选i,第二个人选j(i、j表示质因数的选择情况),g[k][i][j]表示第k+1个人第一个人选i第二个人选j的方案数,在每一类数做之前都用f给g数组赋值,做完这一类后f[i][j]=g[0][i][j]+g[1][i][j]-f[i][j](这里减掉的是两个都没选的情况),这里的循环要倒着来做,有点像分组背包,保证的是同一类数不可能同时出现在两边。

这道题目中处理质因数的方法很巧妙。虽然做题的时候想到了最多只有一个大于根n的质因数,但是没有灵活的用上。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxnode 1<<8
#define LL long long
using namespace std;
struct use{
    int fi,se;
}num[505]={0};
int n;
LL f[maxnode][maxnode]={0},g[2][maxnode][maxnode]={0},prime[9]={0,2,3,5,7,11,13,17,19};
int cmp(const use &x,const use &y){return x.fi<y.fi;}
int next(int i)
{
    if (num[i].fi==1) return i;
    while(num[i].fi==num[i+1].fi&&i<n) ++i;
    return i;
}
int main()
{
    freopen("dinner.in","r",stdin);
    freopen("dinner.out","w",stdout);
    
    int i,j,k,t,tt;LL p,ans=0;
    scanf("%d%lld",&n,&p);
    for (i=2;i<=n;++i)
    {
        k=i;
        for (j=1;j<=8;++j)
        {
            if (k%prime[j]==0)
            {
                num[i].se|=1<<(j-1);
                while(k%prime[j]==0) k/=prime[j];
            }
        }
        num[i].fi=k;
    }
    sort(num+2,num+n+1,cmp);
    f[0][0]=1;
    for (i=2;i<=n;i=tt+1)
    {
        for (j=0;j<=255;++j)
          for (k=0;k<=255;++k)
              g[0][j][k]=g[1][j][k]=f[j][k];
        tt=next(i);
        for (t=i;t<=tt;++t)
          for (j=255;j>=0;--j)
            for (k=255;k>=0;--k)
            {
                if ((j&num[t].se)==0) g[1][j][k|num[t].se]=(g[1][j][k|num[t].se]+g[1][j][k])%p;
                if ((k&num[t].se)==0) g[0][j|num[t].se][k]=(g[0][j|num[t].se][k]+g[0][j][k])%p;
            }
        for (j=0;j<=255;++j)
          for (k=0;k<=255;++k) 
            f[j][k]=((g[0][j][k]+g[1][j][k]-f[j][k])%p+p)%p;
    }
    for (i=0;i<=255;++i)
      for (j=0;j<=255;++j) 
        ans=(ans+f[i][j])%p;
    printf("%lld\n",ans);
    
    fclose(stdin);
    fclose(stdout);
}
View Code

 

DAY2

T1荷马史诗

题目大意:给n个字符安排一个k进制的替代码,要求一个都不是其他的前缀,同时要求最长的替代码最小。

思路:可以转化成k叉哈夫曼树,要求树高尽量小。那么像合并果子那样,用一个优先队列维护,在权值相同的时候,先合并高度小的,最后输出就行了。虽然之前没见过,但在测试时根据样例和贪心,还是可以写出来的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
struct use{
    LL val,dep;
    bool operator <(const use &x)const
    {
        return val==x.val ? dep>x.dep : val>x.val;
    }
};
priority_queue<use> que;
int main()
{
    freopen("epic.in","r",stdin);
    freopen("epic.out","w",stdout);
    
    int n,k,i,j; LL x,ans=0,sum,ll;
    use y;
    scanf("%d%d",&n,&k);
    for (i=1;i<=n;++i)
    {
        scanf("%I64d",&x);
        que.push((use){x,0});
    }
    j=n;
    if ((n-1)%(k-1)>0) j+=k-1-(n-1)%(k-1);
    for (i=n+1;i<=j;++i) que.push((use){0,0});
    while(j>1)
    {
        sum=ll=0;
        for (i=1;i<=k;++i)
        {
            y=que.top();que.pop();
            ans+=y.val;sum+=y.val;
            ll=max(ll,y.dep);
        }
        j-=k-1;que.push((use){sum,ll+1});
    }
    y=que.top();
    printf("%I64d\n%I64d\n",ans,y.dep);
    
    fclose(stdin);
    fclose(stdout);
}
View Code

T2品酒大会

题目大意:给定一个字符串,求lcp(i,j)>=l(l=0~n-1)的对数和val[i]*val[j]的最大值。

思路:后缀数组的题目。跟差异很像,处理出sa,rank,height数组后分治处理一下就可以了。

测试时其他有同学按height从大到小排序,然后用并查集做,很神的做法啊。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#define maxnode 300005
#define inf 0x7fffffffffffffffLL
#define LL long long
using namespace std;
struct use{
    int minn,minp;
}tree[maxnode*4]={0};
struct uu{
    LL maxn,minn;
}sta;
int sa[maxnode]={0},rank[maxnode]={0},c[maxnode]={0},t1[maxnode]={0},t2[maxnode]={0},
    height[maxnode]={0},n,m;
char ss[maxnode];
LL ans[2][maxnode]={0},val[maxnode]={0};
bool cmp(int *y,int a,int b,int k)
{
    int a2,b2;
    a2= a+k>=n ? -1 : y[a+k];
    b2= b+k>=n ? -1 : y[b+k];
    a=y[a];b=y[b];
    return a==b&&a2==b2;
}
void build()
{
    int i,k,p,*x=t1,*y=t2;
    for (i=0;i<m;++i) c[i]=0;
    for (i=0;i<n;++i) ++c[x[i]=(ss[i]-'a')];
    for (i=1;i<m;++i) c[i]+=c[i-1];
    for (i=n-1;i>=0;--i) sa[--c[x[i]]]=i;
    for (k=1;k<=n;k<<=1)
    {
        p=0;
        for (i=n-k;i<n;++i) y[p++]=i;
        for (i=0;i<n;++i) if (sa[i]>=k) y[p++]=sa[i]-k;
        for (i=0;i<m;++i) c[i]=0;
        for (i=0;i<n;++i) ++c[x[y[i]]];
        for (i=1;i<m;++i) c[i]+=c[i-1];
        for (i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);m=1;x[sa[0]]=0;
        for (i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k) ? m-1 : m++;
        if (m>=n) break;
    }
}
void pre()
{
    int i,j,k=0;
    for (i=0;i<n;++i) rank[sa[i]]=i;
    for (i=0;i<n;++i)
    {
        if (!rank[i]) continue;
        if (k) --k; j=sa[rank[i]-1];
        while(ss[i+k]==ss[j+k]) ++k;
        height[rank[i]]=k;
    }
}
use updata(use x1,use x2)
{
    if (x1.minn<=x2.minn) return x1;
    else return x2;
}
uu updata2(uu x1,uu x2)
{
    uu x3;
    x3.minn=min(x1.minn,x2.minn);
    x3.maxn=max(x1.maxn,x2.maxn);
    return x3;
}
void buildt(int i,int l,int r)
{
    int mid;
    if (l==r)
    {
        tree[i].minn=height[l];tree[i].minp=l;return;
    }
    mid=(l+r)/2;
    buildt(i*2,l,mid);buildt(i*2+1,mid+1,r);
    tree[i]=updata(tree[i*2],tree[i*2+1]);
}
use task(int i,int l,int r,int ll,int rr)
{
    int mid; use x1,x2;
    if (ll<=l&&r<=rr) return tree[i];
    x1.minn=x2.minn=x1.minp=x2.minp=2100000000LL;
    mid=(l+r)/2;
    if (ll<=mid) x1=task(i*2,l,mid,ll,rr);
    if (rr>mid) x2=task(i*2+1,mid+1,r,ll,rr);
    return updata(x1,x2);
}
uu work(int l,int r)
{
    if (l>=r) 
    {
       if (l==r) return (uu){val[sa[l]],val[sa[l]]};
       else return sta;
    }
    use x; x=task(1,0,n-1,l+1,r);
    ans[0][x.minn]+=(LL)(x.minp-l)*(LL)(r-x.minp+1);
    uu x1,x2;
    x1=work(l,x.minp-1);x2=work(x.minp,r);
    ans[1][x.minn]=max(ans[1][x.minn],max(x1.maxn*x2.maxn,max(x1.minn*x2.minn,
                   max(x1.maxn*x2.minn,x1.minn*x2.maxn))));
    return updata2(x1,x2);
}
int main()
{
    freopen("savour.in","r",stdin);
    freopen("savour.out","w",stdout);
    
    int i,j;
    scanf("%d",&n);m=26;
    while(1)
    {
        ss[0]=getchar();
        if (ss[0]>='a'&&ss[0]<='z') break;
    }
    for (i=1;i<n;++i) ss[i]=getchar();
    for (i=0;i<n;++i) scanf("%I64d",&val[i]);
    build();pre();buildt(1,0,n-1);
    memset(ans[1],128,sizeof(ans[1]));
    sta.maxn=ans[1][maxnode-1];sta.minn=inf;work(0,n-1);
    for (i=n-1;i>=0;--i)
    {
        ans[0][i]+=ans[0][i+1];
        ans[1][i]=max(ans[1][i],ans[1][i+1]);
    }
    for (i=0;i<n;++i)
      printf("%I64d %I64d\n",ans[0][i],(ans[0][i]==0 ? 0 : ans[1][i]));
      
    fclose(stdin);
    fclose(stdout);
}
View Code

 

posted @ 2015-08-14 17:07  Rivendell  阅读(322)  评论(0编辑  收藏  举报