2022牛客暑期多校训练营1 个人题解

博主Linno分享了解题笔记,包括ACM题目A-Villages:Landlines的解法,通过排序和合并电区解决线路问题。B-SpiritCircleObservation未补,C-SeatAllocation通过区间覆盖计算座位。D-MochaandRailgun利用射线原理计算最优路线。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title :2022牛客暑期多校训练营1 题解
date : 2022-8-16
tags : ACM,练习记录
author : Linno


2022牛客暑期多校训练营1 题解

题目链接 :https://ac.nowcoder.com/acm/contest/33186

补题进度 :7/11

A - Villages: Landlines

签到题,记录做右端点,然后按左边排一下序,合并右端点能够到达的点,否则就再供一次电就行了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct dat
{
	int p,d,l,r;
}a[1000005],b[1000005];
int l,r,n,p[1000005],d[1000005],cnt,ans;
bool cmp(dat a,dat b)
{
	if (a.l!=b.l) return a.l<b.l;
	else return a.r<b.r; 
}
signed main()
{
	cin>>n;
	//cin>>x>>r;
	for (int i=1;i<=n;++i) 
	 {
	 	scanf("%lld%lld",&p[i],&d[i]);
	 	a[i].p=p[i];
	 	a[i].d=d[i];
	 	a[i].l=p[i]-d[i];
	 	a[i].r=p[i]+d[i];
	 }
	sort(a+1,a+1+n,cmp);
	l=a[1].l; r=a[1].r;
	for (int i=2;i<=n;++i)
	 {
	 	if (a[i].l<=r) r=max(r,a[i].r);
		 else {
		 	b[++cnt].l=l;
		 	b[cnt].r=r;
		 	l=a[i].l; 
		 	r=a[i].r;
		 }
	 }
	b[++cnt].l=l;
	b[cnt].r=r;
	for (int i=2;i<=cnt;++i)
	  ans+=b[i].l-b[i-1].r;
	cout<<ans;
	return 0;
}

B - Spirit Circle Observation

没补。

C - Grab the Seat!

范围很小,每次询问单独去求就行了。对于(0,1)和(0,m)到询问的点做两条线,斜率在这之间的点都会被覆盖掉,就可算出每一行可选位置的数量,然后求min得到最终一行可选座位数量。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
struct dat {
	long long x,y;
} a[N];
long long n,m,k,q,p,x,y,pos[N],ans;
double maxx,mini[N];

int main() {
	cin>>n>>m>>k>>q;
	for (int i=1; i<=k; ++i) {
		scanf("%lld%lld",&a[i].x,&a[i].y);
	}
	for (int i=1; i<=q; ++i) {
		scanf("%lld%lld%lld",&p,&x,&y);
		a[p].x=x;
		a[p].y=y;
		for (int j=1; j<=m; ++j)
			pos[j]=n+1,mini[j]=n+1;

		for (int j=1; j<=k; ++j)
			pos[a[j].y]=min(pos[a[j].y],a[j].x);
		ans=0;
		maxx=0;
		for (int j=2; j<=m; ++j) {
			if (pos[j]!=n+1) {
				maxx=max(maxx,(double)(j-1.0)*1.0/pos[j]);
			}
			mini[j]=min(mini[j],(double)(j-1.0)*1.0/maxx);
		}
		mini[m]=min(mini[m],pos[m]*1.0);
		maxx=0;
		for (int j=m-1; j>=1; j--) {
			if (pos[j]!=n+1) {
				maxx=max(maxx,abs((double)(j-m)*1.0/pos[j]/1.0));
			}
			mini[j]=min(mini[j],(double)(j-m)*1.0/(-maxx*1.0));
		}
		mini[1]=min(mini[1],pos[1]*1.0);
		for (int j=1; j<=m; ++j) {
			long long f=floor(mini[j]);
			if (mini[j]-f<1e-8) ans+=f-1;
			else ans+=f;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

D - Mocha and Railgun

直观感觉就是沿原点连线,然后直接射出去的投影是弧长最大的,不会严格证,但这是最好想的切入口。

#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
double R,x,y,r,d,maxi,mini,dl,len,a,ans; 
int T;
int main()
{
    cin>>T;
    while (T--)
    {
        scanf("%lf",&R);
        scanf("%lf%lf%lf",&x,&y,&r);
        d=sqrt(x*x+y*y);
        maxi=sqrt(R*R-(d-r)*(d-r));
        mini=sqrt(R*R-(d+r)*(d+r));
        dl=maxi-mini;
        len=sqrt(dl*dl+2*r*2*r);
        a=asin(len/2.0/R);
        ans=2*a*R;
        printf("%.10lf\n",ans);
    }
    return  0;
}

E - LTCS

待补。

F - Cut

待补。

G - Lexicographical Maximum

签到题,只要看字符串里有没有9即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
string str;

void Solve(){
	cin>>str;
	int len=str.length(),flag=1;
	for(int i=0;i<len-1;++i){
		if(str[i]!='9') flag=0;
	}
	if(flag) cout<<str<<"\n";
	else{
		for(int i=0;i<len-1;++i) cout<<9;
		cout<<"\n";
	}
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

H - Fly

可以转化为背包问题,然后用NTT优化。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=1e5+5e4;
const int G=3,Gi=332748118;
const int lim=40001;

int fpow(int a,int b=mod-2){
	int res=1;
	while(b){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

int a[N],A[N],F[N],g[N];
int n,m,K,cnt[N],r[N<<1];
vector<int>had[62];
int vis[N], tag;

inline int get(int x){
	int len=1;
	while (len<=x) len<<=1;
	return len;
}

void NTT(int *A, int len, int tag){
	for(int i=1;i<=len;i++) r[i]=(r[i>>1]>>1)|((i&1)?(len>>1):0);
	for(int i=0;i<len;i++){
		if (i<r[i])	swap(A[i],A[r[i]]);
	}
	for(int mid=1;mid<len;mid<<=1){
		int wn=fpow(tag?G:Gi,(mod-1)/(mid<<1));
		for(int j=0;j<len;j+=(mid<<1)){
			for (int k=0,w=1;k<mid;k++,w=w*wn%mod){
				int x=A[j+k],y=w*A[j+k+mid]%mod;
				A[j+k]=(x+y)%mod;
				A[j+k+mid]=(x+mod-y)%mod;
			}
		}
	}
	if(tag) return;
	int invlen=fpow(len);
	for(int i=0;i<len;i++) A[i]=A[i]*invlen%mod;
}

signed main() {
	scanf("%d%lld%lld",&n,&m,&K);
	a[0]=1;
	++cnt[1];
	for (int i=1;i<=n;i++) scanf("%d",&a[i]),++cnt[a[i]];
	for (int i=1,x,y;i<=K;i++){
		scanf("%d%d",&x,&y);
		had[y].push_back(x);
	}
	int len=get(lim);
	int w=fpow(3,(mod-1)/len);
	A[0]=1;
	for(int i=1;i<len;i++) A[i]=A[i-1]*w%mod;
	for(int i=0;i<len;i++) F[i]=1;
	for(int i=0;i<=lim;i++){
		if(cnt[i]){
			for (int j=0;j<len;j++){
				F[j]=F[j]*fpow((A[(j*i)&(len-1)]+1)%mod,cnt[i])%mod;
			}
		}
	}
	NTT(F,len,0);
	len=get(2*lim);
	memset(A,0,sizeof(A));
	A[0]=1;
	for(int i=0;(i<=60)&&m;i++){
		memcpy(g,F,sizeof(F));
		++tag;
		for(auto x:had[i]){
			if(vis[x]!=tag){
				vis[x]=tag;
				for(int j=a[x];j<=lim;j++){
					g[j]=(g[j]+mod-g[j-a[x]])%mod;
				}
			}
		}
		NTT(A,len,1);
		NTT(g,len,1);
		for (int j=0;j<len;j++) g[j]=g[j]*A[j]%mod;
		NTT(g,len,0);
		memset(A,0,sizeof(A));
		for(int j=m%2;j<=2*lim;j+=2) A[j>>1]=g[j];
		m>>=1;
	}
	printf("%d", A[0]);
	return 0;
}

I - Chiitoitsu

只需要考虑凑原来手牌里单个的牌,然后 d p [ i ] [ j ] dp[i][j] dp[i][j]表示表示牌库剩i张牌时凑齐j对时的概率,算出每轮摸到单的概率和摸到废牌的概率就可以了,不需要过多的策略。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int dp[205][10];
string str;

inline int fpow(int a,int b){
	int res=1;
	while(b){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

void Solve(){
	cin>>str;
	set<int>cnt;
	for(int i=0;i<str.length();i+=2){
		int hs=str[i]*131+str[i+1];
		cnt.insert(hs);
	}
	int x=cnt.size(),y=13-x;
	cout<<dp[13][y]<<"\n";
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	for(int i=136;i>=13;--i){
		for(int j=0;j<=6;++j){
			int lft=13-2*j;
			int p1=3*lft*fpow(136-i,mod-2)%mod;
			int p2=(1-p1+mod)%mod;
			dp[i][j]=(dp[i+1][j+1]*p1+dp[i+1][j]*p2+1)%mod;
		}
	}
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	for(int i=1;i<=T;++i){
		cout<<"Case #"<<i<<": ";
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

J - Serval and Essay

如果一个点y只能被另一个点x推断出来,那么显然可以合并,并且x继承所有y能推断的点,那么我们就不需要考虑y了,模拟这个过程去反复合并即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
//#define int long long
#define pii pair<int,int>
#define mk make_pair
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

set<int>to[N],from[N];
int n,fa[N],sz[N];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
} 

void Merge(int x,int y){
	x=find(x),y=find(y);
	if(x==y) return;
	if(to[x].size()<to[y].size()) swap(x,y);
	fa[y]=x;
	sz[x]+=sz[y];
	vector<pii> mg;
	for(auto t:to[y]){
		to[x].insert(t);
		from[t].erase(y);
		from[t].insert(x);
		if(from[t].size()==1) mg.emplace_back(mk(t,x));
	}
	for(auto p:mg){
		Merge(p.first,p.second);
	}
}

void Solve(){
	n=read();
	for(int i=1;i<=n;++i) fa[i]=i,sz[i]=1;
	for(int i=1;i<=n;++i){
		int k=read();
		for(int j=1,y;j<=k;++j){
			y=read();
			to[y].insert(i);
			from[i].insert(y);
		}
	}
	for(int i=1;i<=n;++i){
		if(from[i].size()==1){
			Merge(*from[i].begin(),i);
		}
	}
	int ans=0;
	for(int i=1;i<=n;++i) ans=max(ans,sz[i]);
	printf("%d\n",ans);
	for(int i=1;i<=n;++i) to[i].clear(),from[i].clear();
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	T=read();
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	for(int i=1;i<=T;++i){
		printf("Case #%d: ",i);
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

K - Villages: Landcircles

没补。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值