暑假集訓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,是一個時代的終結。

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