暑假集训test12

所以又是一次考试。
然而这次真是莫名的诡异。
拿到题目以后,点开第一题,读了几遍题再结合样例,感觉自己懂了。
然后由于有人对题意意见不一,老师讲了一遍题意。
然而更不知道怎么做了好不好。。。。
然后在老师皱着眉听完一个小时的题意质疑后,果断告诉我们,不!做!了!
于是我们换了一套题。
浪费了一个小时后,紧锣密鼓的做下一套。
嗯哼,在下面。

1.排列的最大长度

题目描述

现有两个长度为 n 的排列 A,B,需再寻找一个排列 C,使得对于 C 中任意两个数i,j(i小于j),满足 Ci在A 中的位置比Cj靠前,在 B 中位置也比 Cj靠前,求这个排列 C 的最大长度。

输入描述

第一行一个数n,表示排列的长度。
第二行 n 个正整数,为A 排列。
第三行 n 个正整数,为B 排列。

输出描述

一行一个数表示排列 C的最大长度。

样例数据

输入
5
1 2 4 3 5
5 2 3 4 1

输出
2

数据范围及提示

C 可以为{2,3},也可{2,4}
对于40%的数据,n<=5000
对于100%的数据,n<=100000
注意:C 是序列不是排列。

第一次看题的时候,感觉一定是最长公共子序列。
然后开二维数组,写了一个O(n^2)的代码,成功过了样例和手写样例。
然后死在了超时和超空间上。
嗯,部分分40。

其实可以将a[i]在b数组中的位置记为num[i],然后求num的最长不下降子序列。
用O(nlogn)复杂度的那种哦~

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int n,ans;
int num[100004],b[100004];

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

int main()
{
    //freopen("t1.in","r",stdin);
    //freopen("t1.out","w",stdout);

    n=read();
    for(int i=1;i<=n;i++)
        num[read()]=i;
    for(int i=1;i<=n;i++)
        b[i]=num[read()];
        memset(num,0,sizeof(num));

    for(int i=1;i<=n;i++)
    {
        int l=1,r=ans;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(b[i]<=num[mid])
                r=mid-1;
            else
                l=mid+1;    
        }
        if(l>ans)
            ans++;
        num[l]=b[i];
    }

    cout<<ans<<endl;
    return 0;   
}

2.覆盖节点

题目描述

现在有一棵 n 个节点的树,要求你选出L 条路径使得这些路径覆盖的节点数最大,求这个最大节点数。

输入描述

第一行两个整数 n,L,分别表示节点数与路径条数。
接下来 n-1 行每行表示一条树边。

输出描述

一行一个整数表示最多覆盖的节点数。

样例数据

输入
17 3
1 2
3 2
2 4
5 2
5 6
5 8
7 8
9 8
5 10
10 13
13 14
10 12
12 11
15 17
15 16
15 10

输出
13

数据范围及提示

Subtask1(30):1<=n<=20,1<=L<=3
Subtask2(30):1<=n,L<=3000
Subtask3(40):1<=n,L<=1000000
注:最终评测开 O2。

这道题是真没思路的,当时就只能乱搞了。

这里写图片描述

嗯,将树分层,叶子节点为第一层,给其他标号,号数为距叶子节点的最远距离。然后取最大者,再取次大者……
嗯就这样。

最优性:画图可知,一条路径每层最多经过2*L 个结点,于是 Ans 为上界。
可行性:将叶子节点那层称为外层,其余为内层,于是注意内层点,当内层点覆盖完全后,余下的叶结点可任意匹配来构成路径,于是可知路径条数不超过L。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#define N 1000010
using namespace std;

struct node
{
    int next;
    int to;
}bian[N*2];
int n,l,x,y,tot,t,h,ans;
int first[N],d[N],q[N],g[N],deep[N];

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

inline void create(int x,int y)     //建边。
{
    tot++;
    bian[tot].next=first[x];
    first[x]=tot;
    bian[tot].to=y;
}

int main()
{
    freopen("t2.in","r",stdin);
    freopen("t2.out","w",stdout);

    n=read(),l=read();
    l=l<<1;
    for(int i=1;i<n;i++)
    {
        x=read(),y=read();
        create(x,y);
        create(y,x);
        d[x]++;
        d[y]++;
    }
    h=t=0;
    for(int i=1;i<=n;i++)
        if(d[i]==1)         //记录叶子节点。
            q[++t]=i;
    while(h!=t)
    {
        h++;
        g[deep[h]]++;    //多一个点加入。
        int x=q[h];
        for(int i=first[x];i;i=bian[i].next)
        {
            if(--d[bian[i].to]==1)//叶子节点之后的一层成为了叶子节点。
                q[++t]=bian[i].to,deep[t]=deep[h]+1;
        }
    }
    for(int i=0;g[i];i++)
    {
        if(l<g[i])
            ans+=l;
        else
            ans+=g[i];
    }
    cout<<ans<<endl;
    return 0;
}

3.系列维护

题目描述

现在需要你维护一个序列a[i] ,一开始都是 0,要支持以下两种操作:
1. U k x 把 a[k] 修改为 x
2. Z c s 在序列上选出 c 个正数,把他们都减去 1,询问能不能做 s 次。
询问不会对序列产生影响。

输入描述

第一行两个正整数 n,m 表示序列长度和操作次数。
接下来 m 行为m 个操作。

输出描述

对于每个询问操作,如果能输出”Yes”(不含引号),不能输出“No”。

样例数据

输入
3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1

输出
No
Yes
No
Yes

数据范围及提示

Subtask1(30): n,m,x<=1000
Subtask2(20): n,m<=1000
Subtask3(10): n=40000,m=40000,x<=10000,只有一个点
Subtask4(20):n=50000,m=200000,x<=30000 (不满)
Subtask5(20):n,m<=1000000
注:最终评测开 O2。

设个数大于 s 的数字有k 个(如果 k大于c,显然是 Yes。这里讨论 (k<=c),那么如果个数小于s 的数字和不小于(c - k) * s,那么一定有解。证明很显然对吧_(:зゝ∠)

用两个树状数组或是线段树即可。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;

int getint()
{
    int i=0,f=1;
    char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')
        f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())
        i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=1e6+5;
struct node
{
    int op,x,y;
}q[N];
int n,m,mx,a[N],b[N],cnt[N<<2];
long long sum[N<<2];

inline void lsh()
{
    sort(b+1,b+mx+1);
    mx=unique(b+1,b+mx+1)-b-1;
    for(int i=1;i<=m;i++)
        q[i].y=lower_bound(b+1,b+mx+1,q[i].y)-b;
}

inline void update(int k)
{
    cnt[k]=cnt[k<<1]+cnt[k<<1|1];
    sum[k]=(long long)sum[k<<1]+sum[k<<1|1];
}

inline void modify(int k,int l,int r,int p,int v)
{
    if(l==r)
    {
        cnt[k]+=v;
        sum[k]+=(long long)v*b[l];
        return;
    }
    int mid=l+r>>1;
    if(p<=mid)
        modify(k<<1,l,mid,p,v);
    else 
        modify(k<<1|1,mid+1,r,p,v);
    update(k);
}

int querycnt(int k,int l,int r,int x,int y)
{
    if(x>y||l>r)
        return 0;
    if(x<=l&&r<=y)
        return cnt[k];
    int mid=l+r>>1;
    if(y<=mid)
        return querycnt(k<<1,l,mid,x,y);
    else if(x>mid)
        return querycnt(k<<1|1,mid+1,r,x,y);
    else 
        return querycnt(k<<1,l,mid,x,mid)+querycnt(k<<1|1,mid+1,r,mid+1,y);
}

long long querysum(int k,int l,int r,int x,int y)
{
    if(x>y||l>r)
        return 0;
    if(x<=l&&r<=y)
        return sum[k];
    int mid=l+r>>1;
    if(y<=mid)
        return querysum(k<<1,l,mid,x,y);
    else if(x>mid)
        return querysum(k<<1|1,mid+1,r,x,y);
    else 
        return (long long)querysum(k<<1,l,mid,x,mid)+querysum(k<<1|1,mid+1,r,mid+1,y);
}

int main()
{
    //freopen("t3.in","r",stdin);
    //freopen("t3.out","w",stdout);
    char c;
    n=getint(),m=getint();
    for(int i=1;i<=m;i++)
    {
        for(c=getchar();c!='U'&&c!='Z';c=getchar());
        if(c=='U')
            q[i].op=0;
        else 
            q[i].op=1;
        q[i].x=getint(),q[i].y=getint();
        b[++mx]=q[i].y;
    }

    lsh();

    for(int i=1;i<=m;i++)
    {
        if(q[i].op==0)
        {
            if(a[q[i].x])
                modify(1,1,mx,a[q[i].x],-1);
            a[q[i].x]=q[i].y;
            modify(1,1,mx,a[q[i].x],1);
            continue;
        }
        if(q[i].op==1)
        {
            q[i].x-=querycnt(1,1,mx,q[i].y,mx);
            long long res=querysum(1,1,mx,1,q[i].y-1);
            if(res/b[q[i].y]>=q[i].x)
                puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

很好。
来自2017.7.27

——我认为return 0,是一个时代的终结。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章