「NowCoder Round X」#55

NowCoderX55.

A. 至至子的等差中项

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int a, b;
signed main() {
	a=read(), b=read();
	printf("%lld\n",2*b-a);
}

B. 至至子的按位与

从高位往低位贪心即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int a, b;
signed main() {
	a=read(), b=read();
	int c=0;
	for(int i=62;~i;--i) {
		int x=(a>>i)&1, y=(b>>i)&1;
		if(x==y) c|=1ll<<i;
	}
	printf("%lld\n",c);
}

C. 至至子的斐波那契

注意到 \(fib_{92}\) 是范围内最大的一项。二分查找即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int T, R, f[105];
void solve() {
	int x=read();
	if(x==1) { puts("1"); return; }
	int l=1, r=92, k=0;
	while(l<r) {
		int mid=(l+r+1)/2;
		if(f[mid]<=x) l=mid; else r=mid-1;
	}
	k=l;
	int ans=abs(f[k]-x)<=abs(f[k+1]-x)? k:k+1;
	printf("%lld\n",ans);
}
signed main() {
	f[1]=f[2]=1;
	for(int i=3;i<=92;++i) f[i]=f[i-1]+f[i-2];
	T=read();
	while(T--) solve();
}

D. 至至子的鸿门宴

由于双方足够聪明,所以最终的局面一定是 \(a_1 = 1,a_2 = 2 , \ldots a_n = n\)

所以判断总操作数 \(\sum_{i=1}^n (a_i - i)\) 的奇偶性即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=1e6+5;
int n;
signed main() {
	n=read();
	int ans=0;
	for(int i=1;i<=n;++i) ans+=read()-i;
	if(ans&1) puts("ZZZ"); else puts("SSZ");
}

E. 至至子的长链剖分

树根一定是最大值,如果最大值不唯一,那么一定无解。

显然最终值为 \(i\) 的节点一定是值为 \(i+1\) 的节点的儿子,而儿子节点的数量一定不多于父节点,否则无解。

然后一顿瞎连就行了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=2e5+5;
int T, n, mx, root, cnt, h[N];
vector<pair<int,int> > p[N], ans;
#define fr first
#define sc second
#define pb push_back
void solve() {
	n=read();
	mx=root=cnt=0;
	for(int i=1;i<=n;++i) {
		h[i]=read(), mx=max(mx,h[i]);
		p[i].clear();
	}
	p[0].clear();
	ans.clear();
	for(int i=1;i<=n;++i) {
		if(h[i]==mx) ++cnt, root=i;
		p[h[i]].pb({h[i],i});
	}
	if(cnt!=1) { puts("-1"); return; }
	for(int i=0;i<mx;++i)
		if(p[i].size()<p[i+1].size())  { puts("-1"); return; }
	for(int i=0;i<mx;++i) {
		int sz=p[i+1].size();
		for(int j=0;j<p[i].size();++j) {
			ans.pb({p[i][j].sc,p[i+1][min(sz-1,j)].sc});
		}
	}
	printf("%lld\n",root);
	for(auto pp:ans) printf("%lld %lld\n",pp.fr,pp.sc);
	
}
signed main() {
	T=read();
	while(T--) solve();
}

F. 至至子的公司排队

每棵树是互相独立的,因此可以分别考虑,合并答案。

对于一棵树 \(i\),设 \(f_x\) 为在以 \(x\) 为根的子树中,满足条件的排列方法数量。

先不考虑每个子节点子树内的情况,设子节点集合为 \(\{y\}\),大小为 \(m\)

转移方法有两种。

一,多重集的全排列。。那么对于 \(y_i\),相当于存在 \(sz_{y_i}\) 个相同元素,总的元素个数为 \(sz_x - 1\),最后对于每个 \(y_i\),其内部的方案数为 \(f_{y_i}\),相乘即可。 \[ f_x = \prod_{i=1} f_{y_i} \frac{(sz_x - 1)!}{\prod_{i=1}^m (sz_{y_i}!)} \] 二,子树合并。其实我也不太明白这种东西是怎么证明的,但是看起来比较直观一些。 \[ f_x = \prod_{i=1}^m f_{y_i} C_{sz_x' - 1}^{sz_{y_i}} \] 其中 \(sz_x'-1 = \sum_{j=1}^i sz_{y_i}\)

 

最后合并不同树的时候,也可以使用上述两种方法。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=1e5+5, mod=1e9+7;
int n, cnt, ans=1, c[N], f[N], fac[N], inv[N], sz[N];
int tot, h[N], to[N<<1], nxt[N<<1];
void add(int x,int y) {
	to[++tot]=y, nxt[tot]=h[x], h[x]=tot;
}
void addedge(int x,int y) {
	add(x,y), add(y,x);
}
int fp(int a,int b) {
	int c=1;
	for(;b;a=a*a%mod,b>>=1) if(b&1) c=c*a%mod;
	return c;
}
void init() {
	fac[0]=fac[1]=1;
	const int d=1e5;
	for(int i=2;i<=d;++i) fac[i]=fac[i-1]*i%mod;
	inv[d]=fp(fac[d],mod-2);
	for(int i=d-1;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int n,int m) {
	if(n<m) return 0;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void dp(int x,int fa) {
	sz[x]=1, f[x]=1;
	int F=1, g=1;
	for(int i=h[x];i;i=nxt[i]) {
		int y=to[i];
		if(y==fa) continue;
		dp(y,x);
		sz[x]+=sz[y];
		(f[x]*=f[y]*C(sz[x]-1,sz[y])%mod)%=mod;
		// (F*=f[y])%=mod, (g*=inv[sz[y]])%=mod;
	}
	// f[x]=(F*fac[sz[x]-1]%mod*g%mod)%mod;
    // 注释掉的是多重集全排列
}
signed main() {
	init();
	n=read();
	for(int i=1;i<=n;++i) {
		c[i]=read();
		tot=0;
		for(int j=0;j<=c[i];++j) h[j]=0;
		for(int j=2;j<=c[i];++j) {
			int p=read();
			addedge(p,j);
		}
		dp(1,0);
		cnt+=c[i];
		(ans*=f[1]*C(cnt,c[i])%mod)%=mod;
        // 这也是子树合并
	}
	printf("%lld\n",ans);
}

「NowCoder Round X」#55
https://yozora0908.github.io/2022/ncx55-solution/
作者
yozora0908
发布于
2022年8月21日
许可协议