2019.11.9 csp-s 考前模拟

是自闭少女lz /lb(泪奔

T1

我可能(呸,一定是唯一一个把这个题写炸了的人

题外话:

我可能是一个面向数据编程选手

作为一个唯一一个写炸T1的人,成功通过多组数据将自己的代码改对/(苦笑

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

最新文章

  1. wps恢复经典模式
  2. 水一道NOIP2002提高组的题【A003】
  3. Servlet获取URL地址
  4. jsp状态管理
  5. 【MPI学习2】MPI并行程序设计模式:对等模式 &amp; 主从模式
  6. linux笔记:文件处理命令touch,cat,more,less,head,tail
  7. [转]NHibernate之旅(7):初探NHibernate中的并发控制
  8. animationWithKeyPath键值对
  9. STM32之呼吸灯实验
  10. oracle创建数据库到2%不动问题
  11. JPA关系映射之many-to-many
  12. Protocol Buffer序列化对比Java序列化.
  13. PCoA主坐标分析
  14. AngularJS进阶(十七)在AngularJS应用中实现微信认证授权遇到的坑
  15. 类LinkedList
  16. 使用systemback制作Ubuntu自定义系统镜像和系统备份(抄)
  17. C# deep copy List
  18. java-vip介绍
  19. 关于pyinstaller打包程序时设置icon时的一个坑
  20. 【BZOJ3832】[POI2014]Rally(拓扑排序,动态规划)

热门文章

  1. 也谈Tcp/Ip协议
  2. [题解] [CF518D] Ilya and Escalator
  3. Inter IPP 绘图 ippi/ipps
  4. 图片上传(前端显示预览,后端php接收)
  5. php下intval()和(int)转换使用与区别
  6. CentOS7——卡在在启动界面
  7. ajax-php跨域请求
  8. linux 之 pthread_create 实现类的成员函数做参数
  9. 找出所有从根节点到叶子节点路径和等于n的路径并输出
  10. JSON-Runoob-工具:Json 格式化工具