首页 > 其他 > 详细

近年NOI题目总结

时间:2019-06-10 19:45:07      阅读:181      评论:0      收藏:0      [点我收藏+]

 

 

NOI2015D1T1

题目大意:$T$ 组数据。在一个程序中有无数个变量 $x_i$。现在有 $n$ 条限制,形如 $x_i=x_j$ 或者 $x_i\ne x_j$。(对于每个限制 $i,j$ 给定)问是否存在一种合法的赋值方案满足所有限制。

$1\le T\le 10,1\le n\le 10^5,1\le i,j\le 10^9$。

普及难度。先把所有编号离散化,然后对于每个相等的限制,把这两个变量塞到一个集合里(并查集)。最后对于每个不等的限制,判断两个变量是否在一个集合里。

时间复杂度 $O(Tn\log n)$。

技术分享图片
#include<bits/stdc++.h>
using namespace std;
int t,n,u[100010],v[100010],op[100010],tmp[200020],fa[200020];
int getfa(int x){
    return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
void comb(int u,int v){
    int fu=getfa(u),fv=getfa(v);
    if(fu!=fv) fa[fu]=fv;
}
bool same(int u,int v){
    int fu=getfa(u),fv=getfa(v);
    return fu==fv;
}
int main(){
    scanf("%d",&t);
    while(t--){
        memset(u,0,sizeof(u));
        memset(v,0,sizeof(v));
        memset(op,0,sizeof(op));
        memset(tmp,0,sizeof(tmp));
        scanf("%d",&n);
        for(int i=1;i<=2*n;i++) fa[i]=i;
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",u+i,v+i,op+i);
            tmp[i*2-1]=u[i];
            tmp[i*2]=v[i];
        }
        sort(tmp+1,tmp+2*n+1);
        unique(tmp+1,tmp+2*n+1);
        bool flag=true;
        for(int i=1;i<=n;i++){
            u[i]=lower_bound(tmp+1,tmp+2*n+1,u[i])-tmp;
            v[i]=lower_bound(tmp+1,tmp+2*n+1,v[i])-tmp;
        }
        for(int i=1;i<=n;i++)
            if(op[i]==1) comb(u[i],v[i]);
        for(int i=1;i<=n;i++)
            if(op[i]==0)
                if(same(u[i],v[i])){
                    flag=false;break;
                }
        if(flag) printf("YES\n");
        else printf("NO\n");
    }
}
View Code

NOI2015D1T2

题目大意:一棵 $n$ 个点的树,点编号从 $0$ 到 $n-1$,$0$ 为根。一开始每个点点权均为 $0$。接下来有 $q$ 个操作:

  • $\text{install}\ u$ 表示将 $u$ 到根的路径上的点点权都变为 $1$;
  • $\text{uninstall}\ u$ 表示将 $u$ 子树中所有点点权都变为 $0$。

每次操作完后,询问有多少个点的点权在这次操作中发生了变化。

$1\le n,q\le 10^5$。

树剖裸题。时间复杂度 $O(n+q\log^2n)$。

技术分享图片
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100010;
struct edge{
    int to,nxt;
}e[maxn];
int n,q,el,dfn,head[maxn];
int dep[maxn],size[maxn],son[maxn],fa[maxn];
int id[maxn],w[maxn],top[maxn];
int sum[maxn<<2],set[maxn<<2];
inline void add(int u,int v){
    e[++el]=(edge){v,head[u]};
    head[u]=el;
}
void dfs1(int u,int f,int d){
    dep[u]=d;
    fa[u]=f;
    size[u]=1;
    int maxson=-1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        dfs1(v,u,d+1);
        size[u]+=size[v];
        if(size[v]>maxson){
            maxson=size[v];son[u]=v;
        }
    }
}
void dfs2(int u,int topf){
    id[u]=++dfn;
    top[u]=topf;
    if(!son[u]) return;
    dfs2(son[u],topf);
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==son[u]) continue;
        dfs2(v,v);
    }
}
inline void pushup(int t){
    sum[t]=sum[t<<1]+sum[t<<1|1];
}
inline void pushdown(int l,int r,int t){
    if(~set[t]){
        int mid=l+r>>1;
        set[t<<1]=set[t];
        set[t<<1|1]=set[t];
        sum[t<<1]=set[t]*(mid-l+1);
        sum[t<<1|1]=set[t]*(r-mid);
        set[t]=-1;
    }
}
void build(int l,int r,int t){
    if(l==r){
        set[t]=-1;return;
    }
    int mid=l+r>>1;
    build(l,mid,t<<1);
    build(mid+1,r,t<<1|1);
}
void setstate(int L,int R,int l,int r,int x,int t){
    if(L>=l && R<=r){
        set[t]=x;sum[t]=x*(R-L+1);return;
    }
    pushdown(L,R,t);
    int mid=L+R>>1;
    if(mid>=l) setstate(L,mid,l,r,x,t<<1);
    if(mid<r) setstate(mid+1,R,l,r,x,t<<1|1);
    pushup(t);
}
int getroot(){
    pushdown(1,n,1);
    return sum[1];
}
int install(int u){
    int pre=getroot();
    while(u){
        setstate(1,n,id[top[u]],id[u],1,1);
        u=fa[top[u]];
    }
    return getroot()-pre;
}
int uninstall(int u){
    int pre=getroot();
    setstate(1,n,id[u],id[u]+size[u]-1,0,1);
    return pre-getroot();
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int x;
        scanf("%d",&x);
        add(x+1,i+1);
    }
    dfs1(1,0,1);dfs2(1,1);
    build(1,n,1);
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        char str[10];int x;
        scanf("%s%d",str,&x);x++;
        if(str[0]==i) printf("%d\n",install(x));
        else printf("%d\n",uninstall(x));
    }
}
View Code

NOI2015D2T1

题目大意:有 $n$ 个字符串,第 $i$ 个在文章中出现了 $w_i$ 次。现在要把每个字符串替换成一个 $k$ 进制字符串(每个字符都是 $0$ 到 $k-1$ 的整数)。假设第 $i$ 个字符串被替换成了 $s_i$,那么要求对于任意 $i\ne j$ 都有 $s_i$ 不是 $s_j$ 的前缀。现在请求出替换后文章最小的长度($\sum w_i|s_i|$),在此基础上求出 $\max(|s_i|)$ 的最小值。

$1\le n\le 10^5,2\le k\le 9,0\le w_i\le 10^{11}$。

实际上就是哈夫曼树的定义。那么求个 $k$ 进制哈夫曼树即可。

时间复杂度 $O(k+n\log n)$。

技术分享图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100010;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
    char ch=getchar();ll x=0,f=0;
    while(ch<0 || ch>9) f|=ch==-,ch=getchar();
    while(ch>=0 && ch<=9) x=x*10+ch-0,ch=getchar();
    return f?-x:x;
}
int n,k;ll ans;
struct hhh{
    ll w;int h;
    bool operator<(const hhh &h)const{
        if(w!=h.w) return w>h.w;
        return this->h>h.h;
    }
};
priority_queue<hhh> pq;
int main(){
    n=read();k=read();
    FOR(i,1,n) pq.push((hhh){read(),1});
    if((n-1)%(k-1)!=0) FOR(i,1,k-1-(n-1)%(k-1)) pq.push((hhh){0,1});
    while(pq.size()!=1){
        ll sum=0;int hei=0;
        FOR(i,1,k){
            hhh h=pq.top();pq.pop();
            sum+=h.w;hei=max(hei,h.h);
        }
        pq.push((hhh){sum,hei+1});ans+=sum;
    }
    printf("%lld\n%d\n",ans,pq.top().h-1);
}
View Code

NOI2015D2T2

题目大意:有一个长度为 $n$ 的小写字母字符串 $s$,第 $i$ 个字符有权值 $a_i$。对于这个字符串的任意两个不同后缀 $p,q$,定义 $lcp(p,q)$ 为两个后缀的最长公共前缀的长度。现在对于每个 $0\le i\le n-1$,求出对于所有 $lcp(p,q)\ge i$ 的 $p,q$,$a_p\times a_q$ 的和和最大值。

$1\le n\le 3\times 10^5,|a_i|\le 10^9$。

之前没过的又臭又长又慢的做法的题解

(想看并查集做法的看别人的题解吧……)

NOI2016D1T1

题目大意:如果一个字符串可以被拆分为 $AABB$ 的形式,其中 $A$ 和 $B$ 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。现在给出一个长度为 $n$ 的小写字母字符串 $S$,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。$T$ 组数据。

$1\le n\le 30000,1\le T\le 10$。

考虑计算以 $i$ 结尾的 $AA$ 有多少个(设为 $a_i$),以 $i$ 开头的 $AA$ 有多少个(设为 $b_i$),答案即为 $\sum a_ib_{i+1}$。

下面以求 $a_i$ 为例。枚举 $A$ 的长度 $l$,然后考虑 $l,2l,3l\dots\lfloor\frac{n}{l}\rfloor l$ 这些位置。

对于 $il$ 和 $(i+1)l$,求出这两个后缀的最长公共前缀和这两个前缀的最长公共后缀,设他们的长度分别为 $x$ 和 $y$。(与 $l$ 取最小值)

技术分享图片

 

(从题解偷张图)

那么会发现,$x+y<l$ 时,红色荧光笔部分是无法匹配的,所以不存在满足要求的 $AA$ 串。

而当 $x+y\ge l$ 时:技术分享图片

发现粉色和棕色的都是合法的 $AA$ 串。也就是说会对绿色荧光笔部分的每一个 $a$ 都产生 $1$ 的贡献。(这个区间是 $[(i+1)l-y+l-1,(i+1)l+x-1]$)

$b$ 同理。

求 $x,y$ 可以用后缀数组做到 $O(1)$,区间加可以用差分做到 $O(1)$。

总时间复杂度 $O(Tn\log n)$。

技术分享图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=30030;
#define PB push_back
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    char ch=getchar();int x=0,f=0;
    while(ch<0 || ch>9) f|=ch==-,ch=getchar();
    while(ch>=0 && ch<=9) x=x*10+ch-0,ch=getchar();
    return f?-x:x;
}
struct Suffix_Array{
    char s[maxn];
    int n,m,cnt[maxn],sa[maxn],rak[maxn],tmp[maxn],h[maxn][17],logt[maxn];
    void radix_sort(){
        MEM(cnt,0);
        FOR(i,1,n) cnt[rak[tmp[i]]]++;
        FOR(i,1,m) cnt[i]+=cnt[i-1];
        ROF(i,n,1) sa[cnt[rak[tmp[i]]]--]=tmp[i];
    }
    void build(char s[]){
        MEM(sa,0);MEM(rak,0);MEM(tmp,0);MEM(h,0);
        n=strlen(s+1);m=26;
        FOR(i,1,n) rak[tmp[i]=i]=s[i]-a+1;
        radix_sort();
        for(int d=1,p=1;p<n;m=p,d<<=1){
            p=0;
            FOR(i,1,d) tmp[++p]=n-d+i;
            FOR(i,1,n) if(sa[i]>d) tmp[++p]=sa[i]-d;
            radix_sort();swap(rak,tmp);
            rak[sa[1]]=p=1;
            FOR(i,2,n) rak[sa[i]]=(tmp[sa[i]]==tmp[sa[i-1]] && tmp[sa[i]+d]==tmp[sa[i-1]+d])?p:++p;
        }
        int k=0;
        FOR(i,1,n){
            if(k) k--;
            for(int j=sa[rak[i]-1];s[i+k]==s[j+k];k++);
            h[rak[i]][0]=k;
        }
        logt[1]=0;FOR(i,2,n) logt[i]=logt[i>>1]+1;
        FOR(j,1,logt[n]+1) FOR(i,1,n-(1<<j)+1) h[i][j]=min(h[i][j-1],h[i+(1<<j-1)][j-1]);
    }
    int LCP(int x,int y){
        x=rak[x];y=rak[y];
        if(x>y) swap(x,y);
        x++;
        int k=logt[y-x+1];
        return min(h[x][k],h[y-(1<<k)+1][k]);
    }
}nor,rev;
int n;
char str[maxn];
ll ans,a[maxn],b[maxn];
int main(){
    for(int t=read();t;t--){
        scanf("%s",str+1);n=strlen(str+1);
        nor.build(str);
        for(int i=1,j=n;i<j;i++,j--) swap(str[i],str[j]);
        rev.build(str);
        MEM(a,0);MEM(b,0);ans=0;
        FOR(l,1,n/2){
            for(int i=l,j=l<<1;j<=n;i+=l,j+=l){
                int x=min(l,nor.LCP(i,j)),y=min(l-1,rev.LCP(n-i+2,n-j+2));
                if(x+y>=l){
                    a[j+x-(x+y-l+1)]++;a[j+x]--;
                    b[i-y+(x+y-l+1)]--;b[i-y]++;
                }
            }
        }
        FOR(i,1,n) a[i]+=a[i-1],b[i]+=b[i-1];
        FOR(i,1,n-1) ans+=a[i]*b[i+1];
        printf("%lld\n",ans);
    }
}
View Code

NOI2016D2T1

题目大意:有 $n$ 个区间 $[l_i,r_i]$。现在你要选出 $m$ 个区间,使得至少有一个整点被所有的这些区间覆盖到。对于一个选取方案价值是所有区间的最大长度与最小长度的差。问最小价值。如果没有合法选取方案输出 $-1$。

$1\le n\le 5\time 10^5,1\le m\le 2\time 10^5,0\le l_i\le r_i\le 10^9$。

近年NOI题目总结

原文:https://www.cnblogs.com/1000Suns/p/10999409.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!