1. 程式人生 > >2018.10.17--多校聯測第二場測試總結

2018.10.17--多校聯測第二場測試總結

T1:貪心裸題,估計100分,實際得分100分,原題戳
T2:Dp,估計50分,實際得分50分,暫時沒有在任何OJ上見過相似的題目。
T3:二維點計數,估計100分,實際得分100分,沒有見過類似題目

T1題解:把區間按r為第一關鍵字,l為第二關鍵字排序,轉折點從小到大排序對於兩個點x和y能滿足當前區間,那麼對於後面的區間只有三種情況:
1、x和y都可用
2、x和y都不可用
3、x不可用,y可用
所以選擇更小的x明顯更優,用stl去找x就可以了,程式碼如下:

#include <set>
#include <cstdio>
#include <algorithm>
using namespace std;
 
const int maxn=2e5+5;
 
int n,m,ans;
multiset<int>s;
multiset<int>::iterator it;
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
struct cows {
    int l,r;
 
    bool operator<(const cows &a) const {
        if(r==a.r)return l<a.l;
        return r<a.r;
    }
}c[maxn];
 
int main() {
    freopen("dream.in","r",stdin);
    freopen("dream.out","w",stdout);
    m=read();n=read();
    for(int i=1;i<=m;i++)
        c[i].l=read(),c[i].r=read();
    sort(c+1,c+m+1);
    for(int i=1;i<=n;i++) {
        int tmp=read();
        s.insert(tmp);
    }
    for(int i=1;i<=m;i++) {
        it=s.lower_bound(c[i].l);
        if(it!=s.end()&&*it<=c[i].r)
            ans++,s.erase(it);
    }printf("%d\n",ans);
    return 0;
}

T2:預處理dp[i][j]表⽰i個點的森林,有j個點在第⼀棵樹的概率,轉移考慮第i個點是否在第⼀棵⼦樹中,我們有狀態轉移⽅程
\[dp[i][j]=dp[i-1][j-1]*(j-1)*inv[i]+dp[i-1][j]*(i-j)*inv[i]\]
考慮修改演算法三中狀態的含義,令f[i][j]表⽰有i個點的樹,深度不超過j的概率,g[i][j]表⽰有i個點的森林,深度不超過j的概率,f[i][j]直接從g[i-1][j-1]轉移
來;g[i][j]考慮列舉第⼀棵樹的⼤⼩k,從⼀棵樹和⼀個森林轉移來,同時還要乘上第⼀棵⼦樹⼤⼩為k的概率,我們有狀態轉移⽅程:
\[g[i][j]=\sum\limits_{k=1}^if[k][j]*g[i-k][j]*dp[i][k]\]


具體的細節可以⻅標程。最後只要⽤f[n][j]-f[n][j-1]就可以得到深度為j的樹的概率。
程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;

int n,pps;
int inv[205],f[205][205];
int dp[205][205],g[205][205];

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

void prepare() {
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=1ll*(pps-pps/i)*inv[pps%i]%pps;
}

int main() {
    n=read(),pps=read();
    prepare();
    dp[1][1]=1;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=i;j++) {
            dp[i][j]=1ll*dp[i-1][j-1]*(j-1)%pps*inv[i]%pps;
            dp[i][j]=(dp[i][j]+1ll*dp[i-1][j]*(i-j)%pps*inv[i]%pps)%pps;
        }
    for(int i=0;i<=n;i++)
        g[0][i]=g[1][i]=f[0][i]=f[1][i]=1;
    g[1][0]=0;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=n;j++) {
            f[i][j]=g[i-1][max(0,j-1)];
            for(int k=1;k<=i;k++)
                g[i][j]=(g[i][j]+1ll*f[k][j]*g[i-k][j]%pps*dp[i][k]%pps)%pps;
        }
    int ans=pps-1;
    for(int i=2;i<=n;i++) {
        int tmp=((f[n][i]-f[n][i-1])%pps+pps)%pps;
        ans=(ans+1ll*tmp*i%pps)%pps;
    }
    ans=(ans%pps+pps)%pps;
    printf("%d\n",ans);
    return 0;
}

T3:對於這道題有個非常簡單的性質然而我考場上居然想了2小時!!!!,那就是對於一個區間[l,r]的答案就是r-l+1-cnt,cnt就是兩端都在[l,r]內的邊的個數。知道這個性質就是個二維偏序裸題了………………
程式碼如下:

#include <cstdio>
#include <algorithm>
using namespace std;
#define low(i) ((i)&(-i))

const int maxn=2e5+5;

int n,q;

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

struct fake {
    int opt,id;
    int l,r,ans;
    
    void init(int a,int _id) {
        l=read(),r=read();
        if(l>r)swap(l,r);
        opt=a;id=_id;
    }
    
    bool operator<(const fake &a)const {
        if(r==a.r)return opt<a.opt; 
        return r<a.r;
    }
}p[maxn*2];

struct Tree_array {
    int c[maxn];
    
    void add(int pos) {
        for(int i=pos;i<=n;i+=low(i))
            c[i]++;
    }
    
    int query(int pos) {
        int sum=0;
        for(int i=pos;i;i-=low(i))
            sum+=c[i];
        return sum;
    }
}T;

bool cmp(fake a,fake b) {
    return a.id<b.id;
}

int main() {
    freopen("icekingdom.in","r",stdin);
    freopen("icekingdom.out","w",stdout);
    n=read(),q=read();
    for(int i=1;i<n;i++)
        p[i].init(0,0);
    for(int i=n;i<n+q;i++)
        p[i].init(1,i);
    sort(p+1,p+n+q);
    for(int i=1;i<n+q;i++) {
        if(p[i].opt) p[i].ans=p[i].r-p[i].l+1-(T.query(p[i].r)-T.query(p[i].l-1));
        else T.add(p[i].l);
    }
    sort(p+1,p+n+q,cmp);
    for(int i=n;i<n+q;i++)
        printf("%d\n",p[i].ans);
    return 0;
}