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; }