codeforces div2 845 CD

题目

C
题意: 给定长度为n的数组a,在其中选择若干个数组成数组b,对于1-m这些数,在b中至少存在一个数是他的倍数。如果不能找到,输出-1;如果能找到,输出数组b的最大值减最小值最小的值.
思路: 感觉很综合,没想到捏。双指针,没想到,寄。对于选定的数组成的数组,我们如何检查是否满足条件呢。因为值域只有1e5,我们可以提前预处理出每个数的因子。通过计数来判断1-m的是否每个数都作为因子出现过。
  那么怎么找到最大值减最小值最小呢?双指针,因为我们不care数组的具体样子。把数组a排序,双指针可以求出以每个数为起点的满足条件的最短区间,记录下最小值。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int mod = 1e9+7;
int n,m,k,T;
vector<int> va[N]; //����
int cnt[N];
int fun(int x)
{
	for(int i=1;i*i<=x;++i)
	{
		if(x%i==0)
		{
			va[x].push_back(i);
			if(i!=x/i) va[x].push_back(x/i);
		}
	}
}
void solve()
{
	cin>>n>>m;
	vector<int> a(n);
	for(int i=0;i<n;++i) cin>>a[i];
	sort(a.begin(),a.end());
	int tot = 0;
	int ans = 1e9;
	for(int l=0,r=0;l<n;++l)
	{
		while(r<n&&tot<m)
		{
			for(int &j:va[a[r]])
			{
				if(j<=m&&++cnt[j]==1) tot++;
			}
			r++;
		}
		if(tot==m) ans = min(ans,a[r-1]-a[l]);
		for(int &j:va[a[l]]) 
		{
			if(j<=m&&--cnt[j]==0) tot--;
		}
	}
	if(ans==1e9) ans = -1;
	cout<<ans<<"\n";
}
signed main(void)
{
	int tot = 0;
	for(int i=1;i<=1e5;++i) fun(i),tot+=va[i].size();
//	cout<<tot<<"?\n";
//	for(int i=1000;i<=1000+100;++i) cout<<va[i].size()<<"\n"; 
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--) solve();
	return 0; 
}

D
题意: 给定一颗有根树,树上每个点的权值为1或者0.每个时刻,树上所有节点的权值变为子节点权值的异或和。对于所有初始情况,从全0到全1,所有时刻树中节点的权值和的和。
思路: 观察发现所有节点每个时刻贡献都是2^n-1,一半的情况是1,一半的情况是0.对于叶子显然是如此。对于非叶节点,也是如此。t=0时,一半是1;t=1,根据其子节点的异或和得到其权值,子节点对应的所有情况是全0到全1的,异或和结果是1的全部情况也就是有Cn1 + Cn3 + …,只选出奇数个1的情况,所以一半的情况是1,贡献为2n-1。每个时刻都是如此,因为子节点总是能囊括全部的情况,直到这个节点的所有子节点权值都变成0,以后结果一直是0了。对于叶子,只产生一个时刻的贡献;对于比叶子高一层的节点,产生两个时刻的贡献。以此类推,每个点的贡献是高度乘以2n-1.
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
const int mod = 1e9+7;
int n,m,k,T;
int d[N];
vector<int> va[N];
void dfs(int cur,int fa)
{
	for(int &j:va[cur])
	{
		if(j==fa) continue;
		dfs(j,cur);
		d[cur] = max(d[cur],d[j]+1);
	}
	d[cur] = max(d[cur],1);
}
ll qpow(ll a,int k)
{
	ll res = 1;
	while(k)
	{
		if(k&1) res = res * a % mod;
		k >>= 1;
		a = a * a % mod;
	}
	return res;
}
void solve()
{
	cin>>n;
	for(int i=1;i<=n;++i) va[i].clear(),d[i] = 0;
	for(int i=0;i<n-1;++i)
	{
		int x,y; cin>>x>>y;
		va[x].push_back(y),va[y].push_back(x);
	}
	dfs(1,0);
	ll t = qpow(2,n-1);
	ll ans = 0;
	for(int i=1;i<=n;++i)
	{
		ans = (ans + t*d[i]%mod)%mod;
	}
	cout<<ans<<"\n";
}
signed main(void)
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--) solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值