2019.11.9 csp-s 考前模擬node
是自閉少女lz /lb(淚奔ios
T1編程
我可能(呸,必定是惟一一個把這個題寫炸了的人數組
題外話:spa
我多是一個面向數據編程選手code
做爲一個惟一一個寫炸T1的人,成功經過多組數據將本身的代碼改對/(苦笑blog
對於某個排列的下一個排列,經過我也不知道是感性仍是「李」性李姐,咱們能夠知道,它必定將當前排列最靠後的一個順序對變成逆序對排序
倒着看就是找最小的一組逆序對遞歸
能夠這樣理解,從後往前看這個序列,當出現\(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
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\)數組,咱們能夠採用優先隊列的方式)
#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迷迷糊糊懵懵懂懂說不上不懂,也說不上懂(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; }