【题解】2021牛客OI赛前集训营-提高组(第二场)

为出题人点赞

T1.

找规律(良心出题人啊)。

假设 a [ i ] ≠ b [ i ] a[i]\neq b[i] a[i]=b[i] 一共有 t m p tmp tmp 对,一定是 0 / 1 0/1 0/1 组合,那么剩下 0 / 1 0/1 0/1 的数量就是 c n t 0 / c n t 1 − t m p cnt_0/cnt_1-tmp cnt0/cnt1tmp 。又因为 c n t 0 / c n t 1 − t m p cnt_0/cnt_1-tmp cnt0/cnt1tmp 一定是偶数,所以统计 0 / 1 0/1 0/1 出现次数的奇偶性就能求出 t m p tmp tmp

时间复杂度 O ( n + q ) O(n+q) O(n+q)

T2.

组合数 / dp 计数。

我们考虑枚举线段的左右端点,并规定端点必选,这条线段的整点有 N = gcd ⁡ ( i , j ) N=\gcd(i,j) N=gcd(i,j) 个。同时求出 k = ⌈ d / ( i / N ) 2 + ( j / N ) 2 ⌉ k=\lceil d/\sqrt{(i/N)^2+(j/N)^2}\rceil k=d/(i/N)2+(j/N)2

那么方案数为 2 ∗ ( N − 1 N − 1 − ( n − 1 ) ∗ ( k − 1 ) ) 2*\binom{N-1}{N-1-(n-1)*(k-1)} 2(N1(n1)(k1)N1) 。 对横竖的情况特判即可。

#include<bits/stdc++.h>
#define db double 
#define ll long long
#define mkp make_pair
#define pii pair<int,int> 
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
const int mod=1e9+7;
const int Maxn=505;
inline int read() {
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9') {
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=(x<<1)+(x<<3)+c-'0';
        c=getchar();
    }
    return x;
}
int gcd(int x,int y) {
	return y==0?x:gcd(y,x%y);
}
int T;
ll C[Maxn][Maxn],res;
int n,r,c,d;
void add(ll &x,ll y) {
	x+=y;
	if(x>=mod) x-=mod;
}
int main() {
//	freopen("data.in","r",stdin);
	for(int i=0;i<=501;i++) {
		C[i][0]=1;
	}
	for(int i=1;i<=501;i++) {
		for(int j=1;j<=501;j++) {
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
		}
	}
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d%d%d",&n,&r,&c,&d);
		if(n==1) {
			printf("%d\n",(r+1)*(c+1));
			continue;
		}
		res=0;
		for(int i=0;i<=r;i++) {
			for(int j=0;j<=c;j++) {
				if(i==0&&j==0) continue;
				int N=gcd(i,j),k=ceil(sqrt(ceil(1.0*d*d/(1.0*(i/N)*(i/N)+1.0*(j/N)*(j/N)))));
				
				if((n-1)*k>N) continue;
				
				if(i==0||j==0) res=(res+C[N-1-(n-1)*(k-1)][n-2]*(r-i+1)%mod*(c-j+1)%mod)%mod;
				else res=(res+2*C[N-1-(n-1)*(k-1)][n-2]%mod*(r-i+1)%mod*(c-j+1)%mod)%mod;
			}
		}
		printf("%lld\n",res);
	}
}

T3.

大水题。

可以发现,一个节点 u u u 可以将子树中的两个序列给拼成一个序列,贪心策略为在 u u u 的子树内拼接成尽可能大的序列,再往父节点转移即可。

写一个启发式合并不就完了嘛。(暴力神器)

时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

#include<bits/stdc++.h>
#define db double 
#define ll long long
#define mkp make_pair
#define pii pair<int,int> 
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
const int mod=1e9+7;
const int Maxn=1e5+5;
inline int read() {
    int x=0,f=1; char c=getchar();
    while(c<'0'||c>'9') {
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=(x<<1)+(x<<3)+c-'0';
        c=getchar();
    }
    return x;
}
int n,id[Maxn],dp[Maxn];
vector<int> g[Maxn];
priority_queue<int> q[Maxn]; 
void merge(int x,int y) {
	int tx=id[x],ty=id[y];
	if(q[tx].size()>q[ty].size()) {
		swap(tx,ty);
		swap(x,y);
	}
	while(q[tx].size()) {
		q[ty].push(q[tx].top());
		q[tx].pop();
	}
	id[x]=id[y];
}
void dfs(int x,int fa) {
    for(auto y:g[x]) {
    	if(y==fa) continue;
    	dfs(y,x);
    	merge(x,y);
	}
	int du=2;
	dp[x]=1;
	while(du&&q[id[x]].size()) {
		dp[x]+=q[id[x]].top();
		q[id[x]].pop();
		du--;
	}
	q[id[x]].push(dp[x]);
}
int main() {
//	freopen("tree2.in","r",stdin);
	int T=read();
	while(T--) {
		n=read();
		for(int i=1;i<=n;i++) {
			g[i].clear();
			while(q[i].size()) q[i].pop();
			id[i]=i;
		}
		for(int i=1;i<n;i++) {
			int u=read(),v=read();
			g[u].push_back(v);
			g[v].push_back(u);
		}
		dfs(1,0);
		printf("%d\n",dp[1]);
	} 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值