校内模拟赛(2019.9.10)
Posted hale522520
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了校内模拟赛(2019.9.10)相关的知识,希望对你有一定的参考价值。
第一道题,很zz的题,不多赘述
简(simple)
【题目描述】
大道至简.这就是出题人没有写题目背景的原因.
给出2n个数字,将它们划分成n组,每组的得分为这一组中两个数字的较小值.
求最大得分.
【输入格式】
第一行一个整数n表示正整数的数目.
接下来一行2n个空格隔开的整数a1,a2…a2n
【输出格式】
一行一个整数表示最大得分.
【样例输入】
2
1 3 1 2
【样例输出】
3
【数据范围】
对于10%的数据:n=2
对于另外20%的数据n<=7
对于另外20%的数据:n<=1000
对于另外20%的数据:ai<=100
对于100%的数据: n<=100000,1<=ai<=10^9
代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+7;
LL m,n,k,p,ans;
LL a[N];
int main()
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
scanf("%lld",&n);
for (int i=1;i<=2*n;i++) scanf("%lld",&a[i]);
sort(a+1,a+2*n+1);
for (int i=n;i>=1;i--) ans+=min(a[i*2],a[i*2-1]);
cout<<ans<<endl;
return 0;
第二题就比较有意思了,写题的时候读错题了,上面明明是最短路径上的边数,我却理解为最短路径的总数,真是zz。。。
单(single) 【题目描述】 单车联通大街小巷.这就是出题人没有写题目背景的原因. 对于一棵树,认为每条边长度为1,每个点有一个权值a[i].dis(u,v)为点u到v的最短路径的边数.dis(u,u)=0.对每个点求出一个重要程度.点x的重要程度b[x]定义为其他点到这个点的距离乘上对应的点权再求和. 即:b[x]=a[1]*dis(1,x)+a[2]*dis(2,x)+....+a[n]*dis(n,x) 现在有很多树和对应的a数组,并求出了b数组.不幸的是,记录变得模糊不清了.幸运的是,树的形态完好地保存了下来,a数组和b数组至少有一个是完好无损的,但另一个数组完全看不清了. 希望你求出受损的数组.多组数据. 【输入格式】 第一行输入一个T,表示数据组数。接下来T组数据。 每组数据的第1行1个整数n表示树的点数.节点从1到n编号. 接下来n-1行每行两个整数u,v表示u和v之间有一条边. 接下来一行一个整数t,表示接下来数组的类型。 t=0则下一行是a数组,t=1则下一行是b数组。 接下来一行n个整数,表示保存完好的那个数组,第i个数表示a[i]或b[i]。 【输出格式】 T行,每组数据输出一行表示对应的a数组或b数组,数组的相邻元素用一个空格隔开。忽略行末空格和行尾回车. 【样例输入】 2 2 1 2 1 17 31 2 1 2 0 31 17 【样例输出】 31 17 17 31 【数据范围】 对于100%的数据,T=5,2<=n<=100000,1<=u,v<=n,保证给出的n-1条边形成一棵树 对于100%的数据,t=0或t=1,1<=a[i]<=100,1<=b[i]<=10^9,t=1时保证给出的b数组对应唯一的一个a数组。 对于100%的数据,单个输入文件不会包含超过2000000个整数,这段话可以理解为,你不必考虑输入输出对程序运行时间的影响。 对于100%的数据,保证答案不会超过int能表示的范围 接下来的表格中描述了每个测试点的具体特征。每个测试点的5组数据均符合表格中对应的特征。 测试点编号 n 特殊限制 1 <=1000 均有t=0 2 <=5 均有t=1,答案中a[i]<=20 3 <=100 均有t=1 4 <=100 均有t=1 5 <=30000 所有边满足v=u+1 6 <=10^5 均有t=0 7 <=10^5 均有t=0 8 <=10^5 无特殊限制 9 <=10^5 无特殊限制 10 <=10^5 无特殊限制
上面是题面,由于子任务是对称的,于是我们分开考虑t=1和t=0的情况
首先先说t=0的情况吧:
我们考虑从他的父亲节点跑到子节点对于子节点的贡献,通过手动作差发现,其结果的改变值仅为这个边左右两个子树的a数组的差
由此我们可以先处理出来每个边左右的差值,然后钦定1为根节点,算出1的答案,然后就可以对所有节点进行转移了,最后输出即可
之后是t=1的情况:
我们沿用上面的思考方式,即考虑单边对于全局的影响,我们发现我们一些性质是可以从链上转移到树上的
我们先考虑一条链的情况,这里我将其转换为一个矩阵进行考虑,发现可以直接对于b数组在父节点与子节点之间作差之后
钦定1为根节点,那么a数组的总和即(所有b数组的差值加上2*b[1])/(n-1),可以用矩阵证明得出。
然后我们重新考虑作差之后得出了什么,不难理解得到,这个sum(即a数组的总和)同样也可以在树上转移的,在此不再赘述
参考代码(有一点卡常,所有加了快读快写):
#include<bits/stdc++.h> #define LL long long #define I inline using namespace std; const int N=4e5+7; struct edge int nx,to; e[N]; int m,n,ans,cnt; int a[N],b[N],head[N],ed[N],T; LL sum; void add_edge(int a,int b) cnt++;e[cnt].nx=head[a];e[cnt].to=b;head[a]=cnt; inline void write(int x) if(x<0)putchar(‘-‘);x=-x; if(x>9) write(x/10); putchar(x%10+‘0‘); I int read() int x=0,f=1;char ch=getchar(); while (ch<‘0‘||ch>‘9‘) if (ch==‘-‘) f=-1;ch=getchar(); while (ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-‘0‘;ch=getchar(); return x*f; I void dfs1(int x,int fa) ed[x]=a[x]; for (int i=head[x];i;i=e[i].nx) int y=e[i].to; if (y==fa) continue; dfs1(y,x); ed[x]+=ed[y]; I void dfs2(int x,int fa) for (int i=head[x];i;i=e[i].nx) int y=e[i].to; if (y==fa) continue; sum+=b[y]-b[x]; dfs2(y,x); I void calc(int x,int dep,int fa) ans+=dep*a[x]; for (int i=head[x];i;i=e[i].nx) int y=e[i].to; if (y==fa) continue; calc(y,dep+1,x); I void build(int x,int fa) for (int i=head[x];i;i=e[i].nx) int y=e[i].to; if (y==fa) continue; b[y]=b[x]-ed[y]; build(y,x); I void build2(int x,int fa) a[x]=ed[x]; for (int i=head[x];i;i=e[i].nx) int y=e[i].to; if (y==fa) continue; ed[y]=(sum-b[y]+b[x])/2; a[x]-=ed[y]; build2(y,x); void solve() LL s=0;memset(ed,0,sizeof(ed)); memset(b,0,sizeof(b)); for (int i=1;i<=n;i++) a[i]=read(),s+=a[i]; dfs1(1,0); for (int i=1;i<=n;i++) ed[i]=ed[i]-(s-ed[i]); calc(1,0,1);b[1]=ans; build(1,0); for (int i=1;i<=n;i++) write(b[i]),putchar(‘ ‘); cout<<endl; void solve2() sum=0;memset(a,0,sizeof(a)); for (int i=1;i<=n;i++) b[i]=read(); dfs2(1,0); sum+=b[1]*2;sum/=n-1;ed[1]=sum; build2(1,0); for (int i=1;i<=n;i++) write(a[i]),putchar(‘ ‘); cout<<endl; int main() freopen("single.in","r",stdin); freopen("single.out","w",stdout); T=read(); while (T--) n=read();memset(head,0,sizeof(head));cnt=0;sum=0; ans=0; for (int i=1;i<n;i++) int x=read(),y=read(); add_edge(x,y);add_edge(y,x); int opt=read(); if (opt==0) solve(); else solve2(); return 0;
第三题,考场上本来写得是n3的DP,以为自己能过60,结果挂成了40,退役预定
题(problem) 【题目描述】 出个题就好了.这就是出题人没有写题目背景的原因. 你在平面直角坐标系上. 你一开始位于(0,0). 每次可以在上/下/左/右四个方向中选一个走一步. 即:从(x,y)走到(x,y+1),(x,y-1),(x-1,y),(x+1,y)四个位置中的其中一个. 允许你走的步数已经确定为n.现在你想走n步之后回到(0,0).但这太简单了.你希望知道有多少种不同的方案能够使你在n步之后回到(0,0).当且仅当两种方案至少有一步走的方向不同,这两种方案被认为是不同的. 答案可能很大所以只需要输出答案对10^9+7取模后的结果.(10^9+7=1000000007,1和7之间有8个0) 这还是太简单了,所以你给能够到达的格点加上了一些限制.一共有三种限制,加上没有限制的情况,一共有四种情况,用0,1,2,3标号: 0.没有任何限制,可以到达坐标系上所有的点,即能到达的点集为(x,y)|x,y为整数 1.只允许到达x轴非负半轴上的点.即能到达的点集为(x,y)|x为非负数,y=0 2.只允许到达坐标轴上的点.即能到达的点集为(x,y)|x=0或y=0 3.只允许到达x轴非负半轴上的点,y轴非负半轴上的点以及第1象限的点.即能到达的点集为(x,y)|x>=0,y>=0 【输入格式】 一行两个整数(空格隔开)n和typ,分别表示你必须恰好走的步数和限制的种类.typ的含义见【题目描述】. 【输出格式】 一行一个整数ans,表示不同的方案数对10^9+7取模后的结果. 【样例输入0】 100 0 【样例输出0】 383726909 【样例输入1】 100 1 【样例输出1】 265470434 【样例输入2】 100 2 【样例输出2】 376611634 【样例输入3】 100 3 【样例输出3】 627595255 【数据范围】 10%的数据,typ=0,n<=100 10%的数据,typ=0,n<=1000 5%的数据, typ=0,n<=100000 10%的数据,typ=1,n<=100 10%的数据,typ=1,n<=1000 5%的数据, typ=1,n<=100000 10%的数据,typ=2,n<=100 15%的数据,typ=2,n<=1000 10%的数据,typ=3,n<=100 10%的数据,typ=3,n<=1000 5%的数据, typ=3,n<=100000 以上11部分数据没有交集. 100%的数据,保证n为偶数,2<=n<=100000,0<=typ<=3.
同样的,我们考虑每一个子任务:
对于opt=0的情况,我们只需要枚举在x轴方向上走了多少步,y轴上走了多少步,直接组合数一搞即可
对于opt=1的情况,很自然的想到,这是一个经典的catalan数模型
对于opt=2的情况,事情变得棘手了,我们需要考虑的问题便多了,我们用dp[i]表示用i步之后可以回到原点,那么只需要考虑四个方向的单独情况,直接catalan数累加即可
对于opt=3的情况,我们同样按照,枚举x轴,y轴方向上的步数,组合数和catalan数乘出来即可
#include<bits/stdc++.h> using namespace std; #define LL long long const int N=1e5+7; const int Mod=1e9+7; int n,opt; LL inv[N],s[N],dp[N]; LL ans; LL pown(LL a) LL b=Mod-2,res=1; while (b) if (b&1) res=res*a%Mod; a=a*a%Mod;b>>=1; return res; void init() s[0]=1; for (int i=1;i<=n;i++) s[i]=s[i-1]*i%Mod; inv[n]=pown(s[n]); for (int i=n;i>=1;i--) inv[i-1]=inv[i]*i%Mod; LL C(LL a,LL b) return s[a]*inv[b]%Mod*inv[a-b]%Mod; LL catalan(int p) return C(2*p,p)*pown(p+1)%Mod; LL solve0() LL ans=0; for (int i=0;i<=n;i+=2) ans=(ans+C(n,i)*C(i,i/2)%Mod*C(n-i,(n-i)/2)%Mod)%Mod; return ans; LL solve1() LL ans=catalan(n/2); return ans; LL solve2() LL ans;dp[0]=1;dp[2]=4; for (int i=4;i<=n;i+=2) for (int j=2;j<=i;j+=2) dp[i]=(dp[i]+catalan(j/2-1)*4%Mod*dp[i-j]%Mod)%Mod; return dp[n]; LL solve3() LL ans=0; for (int i=0;i<=n;i+=2) ans=(ans+C(n,i)*catalan(i/2)%Mod*catalan((n-i)/2))%Mod; return ans; int main() freopen("problem.in","r",stdin); freopen("problem.out","w",stdout); scanf("%d%d",&n,&opt); init(); if (opt==0) ans=solve0(); if (opt==1) ans=solve1(); if (opt==2) ans=solve2(); if (opt==3) ans=solve3(); printf("%lld\n",ans); return 0;
以上是关于校内模拟赛(2019.9.10)的主要内容,如果未能解决你的问题,请参考以下文章