【过题记录】8.6 (牛客,hd补题)

Ball

稍微推一下就知道要么选择左端点要么选择右端点

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8, pi = acos(-1.0);
#define pb push_back
int t;
double dist(int x1, int y1, int x2, int y2)
{
    return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}
ll dist2(int x1, int y1, int x2, int y2)
{
    return pow(x1 - x2, 2) + pow(y1 - y2, 2);
}
void solve()
{
    int l, x, y;
    cin >> l >> x >> y;
    if (dist2(x, y, 0, 0) <= pow(l, 2))
    {
        cout << "Yes" << endl;
        cout << 0 << endl;
        return;
    }
    if (dist2(x, y, l, 0) <= pow(l, 2))
    {
        cout << "Yes" << endl;
        cout << l << endl;
        return;
    }
    cout << "No" << endl;
    return;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("D:/in.txt", "r", stdin);
    freopen("D:/out.txt", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin >> t;
    while (t--)
        solve();
    return 0;
}


Fight Against the Monster

二分答案
如果用一次创造,一次会少(m-k)个
但是要注意最后如果所剩的小于m个了,就不能再继续创造
所以总共的创造次数就是
n u m = ( x − m ) / ( m − k ) + 1 num=(x-m)/(m-k)+1 num=(xm)/(mk)+1
所以多创造出的就是 n u m ∗ k num*k numk
判断 x + n u m ∗ k x+num*k x+numk与h的关系即可


#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,m,k,h,l,r,mid;
inline int read(){
	char g=getchar();
	int s=0;
	while (g<'0'||g>'9')g=getchar();
	while (g>='0'&&g<='9'){
		s=(s<<3)+(s<<1)+g-'0';
		g=getchar();
	}
	return s;
}
bool check(int x){
	int maxn=x;
	int now=x;
	int u;
	maxn=x+((x-m)/(m-k)+1)*k;
	if (maxn>=h)return true;
	return false;
}
signed main(){
	T=read();
	while (T--){
		m=read();
		k=read();
		h=read();
//		scanf("%d%d%d",&m,&k,&h);
		if (h<=m){
			printf("%lld\n",h);
			continue;
		}
		if (k>=m){
			printf("%lld\n",m);
			continue;
		}
		l=m;r=h;
		while (l<=r){
			mid=(l+r)>>1;
			if (check(mid))r=mid-1;
			else l=mid+1;
		}
		printf("%lld\n",l);
	}
	return 0;
} 

树上 MEX 问题

对于这道题而言
一个数MEX=x的贡献就是x*NUM(x)
同时发现MEX是有一种包含关系的
因为你如果想要令 M E X = x MEX=x MEX=x,就必须同时包含0,1……x-1那么多个元素
所以利用这些包含性质,这个问题的答案就可以变成
∑ i = 1 n − 1 M E X ( x > = i ) \sum_{i=1}^{n-1} MEX(x>=i) i=1n1MEX(x>=i)
想要求解这个问题,我们首先以0号点对应节点为0做一个树形dp求出每个子树内的联通快的数量
d p u = π ( d p v + 1 ) dp_u=\pi (dp_v+1) dpu=π(dpv+1)

然后我们令 c n t i cnt_i cnti表示mex值>=i时的联通快个数
同时维护一个集合 S i S_i Si表示MEX=i时包含哪几个点
初始时 C n t 1 = d p [ 0 ] , S 1 = 0 Cnt1=dp[0],S_1={0} Cnt1=dp[0],S1=0
枚举下一个对应的点 x x x
如果这个点在集合内, C n t x = C n t x − 1 . S i = S i − 1 Cnt_x=Cnt_{x-1}.S_i=S_{i-1} Cntx=Cntx1.Si=Si1
不然的话,这个点一定要被选中
所以我们就找一条路径,使得这个路径包含这个点
如何寻找这个路径?
让这个点一直跳父亲,知道这个父亲在集合内,这就是刚好包含这个点的一条路径,同时将路径上的点放入集合内
那么如何更新 C n t 呢? Cnt呢? Cnt呢?
对于之前的答案,我们对于路径上的点,都是乘以 ( d p x + 1 ) (dp_x+1) (dpx+1)
表示我们可以选择这个子树,也可以不选
但是这个时候,我们想要选取这些点,是不是就意味着,我们就必须选择这些点?
也就是原来的乘以 d p x + 1 dp_x+1 dpx+1改为乘以 d p x dp_x dpx
重复以上过程即可
对于复杂度,每一个点最多被加入一次删除一次
因此总体复杂度为 O ( n l o g P ) O(nlogP) O(nlogP)
其中 l o g P logP logP的复杂度是求逆元带来的


#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N = 1e5+100;
const int P = 998244353;

int n;
int f[N],num[N],v[N];
bool vi[N];
int rt;
int cnt[N];
int fa[N];
struct Node{
	int y,Next;
}e[2*N];
int len,Linkk[N];

void Dfs(int x,int faa){
	int s = 1;
	fa[x] = faa;
	for (int i = Linkk[x]; i; i = e[i].Next){
		int y = e[i].y; if (y == faa) continue;
		Dfs(y,x);
		s = s*(f[y]+1)%P;
	}
	f[x] = s;
}

int Power(int x,int y){
	int s = 1;
	while (y){
		if (y&1) s = s*x%P;
		x = x*x%P;
		y>>=1;
	}
	return s;
}

void Calc(int x){
	if (vi[num[x]]){
		cnt[x] = cnt[x-1];
		return;
	}
	int now = x,la;
	stack < int > st;
	int s = cnt[x-1];
	now = num[x];
	while (vi[now] == 0) vi[now] = 1 , st.push(now) , now = fa[now];
	while (st.size()){
		int xx = st.top(); st.pop();
		s = s*Power((f[xx]+1)%P,P-2)%P;
		s = s*f[xx]%P;
	}
	cnt[x] = s;
	return ;
}


void Insert(int x,int y){
	e[++len] = (Node){y,Linkk[x]};
	Linkk[x] = len;
}

void Work(){
	cin>>n;
	for (int i = 1; i <= n; i++) vi[i] = 0,Linkk[i] = 0,cnt[i] = 0;
	len = 0;
	for (int i = 1; i <= n; i++)
	  cin>>v[i],num[v[i]] = i;
	for (int i = 1,x,y; i < n; i++) cin>>x>>y,Insert(x,y),Insert(y,x);
	rt = num[0];
	Dfs(rt,0);
	cnt[0] = f[rt]; vi[rt] = 1;
	for (int i = 1; i < n; i++)
	  Calc(i);
	int s = 0;
	for (int i = 0; i < n; i++) s = (s+cnt[i])%P;
	cout<<s<<endl;
	return;
}

signed main(){
	cin.tie(0);
	ios::sync_with_stdio(false);
	int t; cin>>t; while(t--) Work();
	return 0;
}
/*
cnt = 15
cnt = 15
cnt = 12
cnt = 12
cnt = 6
cnt = 6
*/
  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值