2019.11.9 csp-s 考前模擬

2019.11.9 csp-s 考前模擬node

是自閉少女lz /lb(淚奔ios

T1編程

我可能(呸,必定是惟一一個把這個題寫炸了的人數組

題外話:spa

我多是一個面向數據編程選手code

做爲一個惟一一個寫炸T1的人,成功經過多組數據將本身的代碼改對/(苦笑blog

SOLUTION:

對於某個排列的下一個排列,經過我也不知道是感性仍是「李」性李姐,咱們能夠知道,它必定將當前排列最靠後的一個順序對變成逆序對排序

倒着看就是找最小的一組逆序對遞歸

能夠這樣理解,從後往前看這個序列,當出現\(a_i<a_{i+1}\)時(由\(a_n\to a_{i+1}\)看a的值是遞增的),就必定會出現一個逆序對隊列

而後咱們在\(a_{i+1} \to a_n\)中找到最小的\(>=a_i\)的數,交換它們的位置,而後將\(a_{i+1}\to a[n]\)的數按從小到大排序即爲答案。

感性李姐

如下是鄙人的垃圾歸併排序式代碼:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}

int n,a[100010],d[100010];
int p=214748364,q=-1;

bool msort(int x,int y) {
    if(x==y) return 0;
    int mid=(x+y)>>1;
    bool bj=msort(x,mid);
    //由於要優先在最後選取,又由於倒序儲存,因此先遞歸左區間
    if(bj) return 1;
    int i=x,j=mid+1;
    while(i<=mid&&j<=y) {
        if((a[i]>a[j]&&q==-1)||(q!=-1&&a[i]>a[q])) {
            //找到了正序中知足a_q<=a_{q+1}的a_q或者找到了更小的>a_q的p
            p=min(p,i);//找一個下標最小
            //由於a_q以後是有序的因此找下標便可
            if(q==-1)
                q=j;//找到a_q之後就不能再次修改q了
            return 1;
        } else {
            i++;
        }
    }
    msort(mid+1,y);
    return 0;
}

bool cmp(int a,int b) {
    return a>b;
}

int main() {
    freopen("permutation.in","r",stdin);
    freopen("permutation.out","w",stdout);
    n=read();
    for(int i=n;i>=1;i--) {//倒序儲存,從原序列的最後開始查找
        a[i]=read();
    }
    msort(1,n);
    if(p==214748364&&q==-1) {//是最後一個排列
        for(int i=1;i<=n;i++)
            printf("%d ",a[i]);
        return 0;
    }
    swap(a[p],a[q]);
    sort(a+1,a+q,cmp);
    for(int i=n;i>=1;i--) 
        printf("%d ",a[i]);
    return 0;
}

而後附上神仙STD:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
int ai[N];
bool use[N]={false};
int main(){
    freopen("permutation.in","r",stdin);
    freopen("permutation.out","w",stdout);
    
    int n,i,j,t,nt;
    scanf("%d",&n);
    for (i=1;i<=n;++i) scanf("%d",&ai[i]);
    for (i=n;i;--i)
        if (ai[i]!=n-i+1) break;
    if (i){
        for (i=n;i>1;--i){
            use[ai[i]]=true;
            if (ai[i-1]<ai[i]){
                t=ai[i-1];
                for (j=ai[i-1]+1;j<=n;++j)
                    if (use[j]) break;
                ai[i-1]=j;use[j]=false;
                use[t]=true;nt=i-1;
                for (j=1;j<=n;++j)
                    if (use[j]) ai[++nt]=j;
                break;
            }
        }for (i=1;i<=n;++i) printf("%d ",ai[i]);
        printf("\n");
    }else{
        for (i=1;i<=n;++i) printf("%d ",i);
        printf("\n");
    }
    
    fclose(stdin);
    fclose(stdout);
}

T2

SOLUTION

30pts 暴力搜索:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}
const int mxn=1010;

int n;
int xl;
struct node {
    int to,nxt;
}e[mxn<<1];
int ecnt,head[mxn];
void add(int from,int to) {
    ++ecnt;
    e[ecnt].to=to;
    e[ecnt].nxt=head[from];
    head[from]=ecnt;
}
vector<int> lj[mxn][mxn],Dfs;

int dep[mxn],siz[mxn],yezi;
int fa[mxn];

void dfs1(int u,int f) {
    dep[u]=dep[f]+1;
    siz[u]=1;
    fa[u]=f;
    int maxn=-1;
    for(int i=head[u],v;i;i=e[i].nxt) {
        v=e[i].to;
        if(v==f) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
    }
    if(siz[u]==1) yezi++;
}

bool vis[mxn];
void dfs(int u,int from) {
    for(int i=0;i<Dfs.size();i++) 
        lj[from][u].push_back(Dfs[i]);
    for(int i=head[u],v;i;i=e[i].nxt) {
        v=e[i].to;
        if(vis[v]) continue;
        vis[v]=1;
        Dfs.push_back(v);
        dfs(v,from);
        Dfs.pop_back();
    }
}

int cnt[mxn][mxn];

int main() {
    freopen("climb.in","r",stdin);
    freopen("climb.out","w",stdout);
    n=read();
    for(int i=1,u,v;i<n;i++) {
        u=read();
        v=read();
        add(u,v);
        add(v,u);
    }
    dfs1(1,0);
    for(int i=1;i<=n;i++){
        memset(vis,0,sizeof(vis));
        Dfs.push_back(i);
        vis[i]=1;
        dfs(i,i);
        Dfs.pop_back();
    }
    int ans[mxn<<5],zz;
    int nx=1;
    ans[1]=1;
    zz=1;
    bool bj=0;
    for(int i=1;i<=yezi;i++) {
        xl=read();
        for(int j=0,nxx=nx;j<lj[nx][xl].size();j++) {
            int v=lj[nx][xl][j];
            if(j!=0) {ans[++zz]=v;
            cnt[nxx][v]++;
            cnt[v][nxx]++;
        }
            
            if(cnt[nxx][v]>2||cnt[v][nxx]>2) {
                bj=1;
                break;
            }
            nxx=v;
        }
        nx=xl;
    }
    for(int j=0,nxx=1;j<lj[nx][1].size();j++) {
        int v=lj[nx][1][j];
        if(j!=0) {ans[++zz]=v;
        cnt[nxx][v]++;
        cnt[v][nxx]++;
    }
    
        if(cnt[nxx][v]>2||cnt[v][nxx]>2) {
            bj=1;
            break;
        }
            nxx=v;
    }
    if(bj) printf("-1");
    else {
        for(int i=1;i<=zz;i++) 
            printf("%d ",ans[i]);
    }
    return 0;
}

100 pts:

考慮給每一個葉子賦值,按照題目中給出的遍歷順序,從小到大分別賦值\(1\to cnt_{yezi}\),記錄數組vec[i],表示第i號節點(葉子節點)的遍歷順序(因此好像vec是不滿的)對於任意一個節點,維護兩個信息:以此節點爲根的子樹內,vec的最大值和最小值;

若是對一個點來講:\(vec_{max}-vec_{min}\ne siz_{yezi}\)(其中\(siz_{yezi}\)表示以點爲根的子樹中葉子節點的數量),那麼就是無解的,輸出‘-1’;(也就是最大最小值之差!=葉子節點個數時無解)

若是有解,如何輸出答案?

首先咱們必然是先要遍歷題中遍歷順序較早的,那麼它對應子樹的\(vec_{min}\)也會相應較小,所以咱們每次遍歷\(vec_{min}\)最小的,順序輸出便可;(對於從小到大遍歷\(vec\)數組,咱們能夠採用優先隊列的方式)

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>

using namespace std;

inline int read() {
    int ans=0;
    char last=' ',ch=getchar();
    while(ch>'9'||ch<'0') last=ch,ch=getchar();
    while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
    if(last=='-') ans=-ans;
    return ans;
}
const int mxn=101000;

int n;
int num[mxn],xl;
struct E {
    int to,nxt;
}e[mxn<<1];
int ecnt,head[mxn];
void add(int from,int to) {
    ++ecnt;
    e[ecnt].to=to;
    e[ecnt].nxt=head[from];
    head[from]=ecnt;
}
struct node {
    int mx,mn;
}d[mxn];

int dep[mxn],siz[mxn],yezi;
int fa[mxn],Siz[mxn];
bool isye[mxn];

void dfs(int u,int f) {
    //printf("%d %d\n",u,f);
    Siz[u]=1;
    for(int i=head[u],v;i;i=e[i].nxt) {
        v=e[i].to;
        if(v==f) continue;
        dfs(v,u);
        Siz[u]+=Siz[v];
    }
    if(Siz[u]==1) yezi++;
}
bool bj;
void dfs1(int u,int f) {
    dep[u]=dep[f]+1;
    fa[u]=f;
    if(bj==1) return;
    d[u].mn=yezi+10;
    for(int i=head[u],v;i;i=e[i].nxt) {
        v=e[i].to;
        if(v==f) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        d[u].mn=min(d[u].mn,d[v].mn);
        d[u].mx=max(d[u].mx,d[v].mx);
    }
    
    if(Siz[u]==1) {
        siz[u]=1;
        d[u].mn=d[u].mx=num[u];
        isye[u]=1;
    }
    if(d[u].mx-d[u].mn+1!=siz[u]) bj=1;
    return ;
}

struct Node{
    int ll,ord;
}hed;

inline bool operator < (const Node &a,const Node &b)
{
    return a.ll>b.ll;
}

void dfs2(int u) {
    printf("%d ",u);
    if(isye[u]) 
        return;
    priority_queue<Node> q;
    for(int i=head[u],v;i;i=e[i].nxt) {
        v=e[i].to;
        if(v==fa[u]) continue;
        q.push(Node{d[v].mn,v});
    }
    while(!q.empty()) {
        Node p=q.top();
        q.pop();
        dfs2(p.ord);
        printf("%d ",u);
    }
}

int main() {
    freopen("climb.in","r",stdin);
    freopen("climb.out","w",stdout);
    n=read();
    for(int i=1,u,v;i<n;i++) {
        u=read();
        v=read();
        add(u,v);
        add(v,u);
    }
    dfs(1,0);
    for(int i=1;i<=yezi;i++) {
        xl=read();
        num[xl]=i;
    }
    dfs1(1,0);
    if(bj==1) printf("-1");
    else {
        dfs2(1);
    }
    return 0;
}

T3:

SOLUTION:

對於這道題的solution迷迷糊糊懵懵懂懂說不上不懂,也說不上懂(wtnl

因此我要複製solution了\xk(笑哭

題解亂碼了*

首先咱們要先對兩個數組排序

而後經過預處理處理出數組p[i]表示b[x]<a[i]的b的極大個數(講簡單一點就是有多少個b[x]是小於a[i]的

\(f[i][k]\)表示對於前i個派,至少有k組a[i]>b[j]的方案;

爲何是至少呢?由於在這裏咱們只是枚舉A前i個位置,B的全部位置,而且知足A>B的k個A分配了B,剩餘的A與B是沒有配對的,而這些人機之間會不會又產生新的配對,咱們是沒有辦法肯定的,但咱們確認已經有k對了,所以是至少。

那麼對於\(f[i][k]=f[i-1][k]+f[i-1][k-1]\times(p[i]-(k-1))\)

如何理解?

\(f[i-1][k]\)表示的是前i-1個派至少有k對\(a_p>b_q\),此時咱們不給\(a_i\)配對

\(f[i-1][k-1]\)表示的是前i-1個派至少有k-1對\(a_p>b_q\),這個時候咱們須要給\(a_i\)配對,那麼可讓\(a_i\)和誰配對呢?顯然咱們剛剛預處理的p數組就有用了,顯然由於序列都是遞增的,所以前k-1個\(a_k\)的配對也必定在p[i]數組所包含的範圍內,因此除去那k-1個已經與前k-1個\(a_k\)配對的\(b_l\),還有剩餘\(p[i]-(k-1)\)\(b_l\)可供選擇,那麼\(a_i\)的配對方案就有\(p[i]-(k-1)\)個。

而後這顯然不是最後的答案

咱們設g[i]表示前n個派,剛好有i組a[x]>b[x];

容斥一下:

\(g[i]=f[n][i]*(n-i)!-g[j]\times C_j^i\)

感性理解:

\(f[n][i]\)分配了i個A,剩餘的A與B有\(A_{n-i}^{n-i} \ \ 即 (n-i)!\)種可能的搭配,而後容斥減掉\(g[j]\times C_j^i \ \ \ j\in[i+1,n]\)

最後答案是g[s];

s-(n-s)=k;

得s=(n+k)/2;

n+k爲奇數時答案爲0;

//放棄掙扎,瞧着可讀性極低的std:
#include<iostream>
#include<cstdio>
#include<algorithm> 
#define N 2010
#define P 1000000009 

using namespace std;

int i,j,a[N],b[N],s[N],n,k,t,p;
long long f[N][N],g[N],c[N][N],fa[N];

int main() {
    freopen("pie.in","r",stdin);
    freopen("pie.out","w",stdout); 
    scanf("%d%d",&n,&k);
    t=(n+k)/2;
    if ((n+k)%2) {
        cout<<0<<endl;
        return 0;
    }
    for (i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for (i=1;i<=n;i++)
        scanf("%d",&b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    for (i=1,p=1;i<=n;i++) {
        while(p<=n&&b[p]<a[i])
            p++;
        s[i]=p-1;
    }
    for(i=0;i<=n;i++) {
        c[i][0]=1;
        for(j=1;j<=i;j++) 
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%P;
    }
    for(fa[0]=1,i=1;i<=n;i++)
        fa[i]=fa[i-1]*i%P;  
    for(f[0][0]=f[1][0]=1,i=1;i<=n;i++,f[i][0]=1)
        for(j=1;j<=i;j++)f[i][j]=(f[i-1][j]+f[i-1][j-1]*max(s[i]-(j-1),0))%P;
    for(i=n;i>=t;i--){  
        g[i]=f[n][i]*fa[n-i]%P;  
        for(j=i+1;j<=n;j++)
            (g[i]+=P-g[j]*c[j][i]%P)%=P;//g[i]=(g[i]-(g[j]*c[j][i]%P)+P)%P  
    }  
    cout<<g[t]<<endl;
    return 0;
}
相關文章
相關標籤/搜索