Codeforces Round 975 (Div. 2) A-E

人生中第一次正常\(div2\)爆写5题。cf给我正反馈最大的一次

A直接找到最大的数字的位置,然后判断一下这个数字的位置下标的奇偶性就好了。然后如果有多个最大的就取奇数位置的。这样可以算出来一定是最优解

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
int n,a[101],f[101][101];
int main()
{
	int T=read();
	while(T--)
	{
		n=read();
		int Max=0,Maxid;
		for(int i=1;i<=n;i++)
		{
			a[i]=read();
			if(Max<=a[i])
			{
				if(Max==a[i])
				{
					Maxid=max(Maxid,i%2);
				}
				else Maxid=i%2;
				Max=a[i];
			}
		}
//		cout<<Maxid<<endl;
		if(n%2==0)
		{
			cout<<Max+n/2<<endl;
		}
		else
		{
			cout<<Max+n/2+(Maxid)<<endl;
		}
	}
	return 0;
}

B画个图,能看出来每个点的被覆盖次数就是左边的点数$\times \(右边点数,如果这个位置上本身就有点,就是\)(左边点数+1) \times (右边点数+1)-1\(。然后可以直接把这个放到一个下表为\)k\(的map中,询问就\)O(log(n))\(查询,总体\)(q\log n)$

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,q,x[100001];
ll k[100001];
map<ll,ll> ma;
int main()
{
	ll T=read();
	while(T--)
	{
		n=read();q=read();
		for(ll i=1;i<=n;i++)
		{
			x[i]=read();
		}
		sort(x+1,x+1+n);
		ma.clear();
		for(ll i=1;i<n;i++)
		{
			ll sum=x[i+1]-x[i]-1;
			ma[i*(n-i)]+=sum;
			ma[(i+1)*(n-i)-1]++;
		}
		ma[n-1]++;
		for(ll i=1;i<=q;i++)
		{
			k[i]=read();
		}
		for(ll i=1;i<=q;i++)
		{
			cout<<ma[k[i]]<<' ';
		}
		cout<<endl;
	}
	return 0;
}

C人类智慧...卡我好久,和很久之前做的一个题目很像。这也导致我想歪了。和整除分块有一点点类似,但是还是不一样,把画个图把不等式写出来就结束了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,k,a[200001];
int main()
{
	ll T=read();
	while(T--)
	{
		n=read(),k=read();
		ll Max=0;ll sum=0;
		for(ll i=1;i<=n;i++)
		{
			a[i]=read();
			Max=max(Max,a[i]);
			sum+=a[i];
		}
		ll rsum=sum+k;
		ll ans=0;//×î½Ó½üµÄ¿¨×éÊý 
		for(ll i=n;i>=1;i--)//ºñ¶È 
		{
			if((rsum/i)>=((sum+i-1)/i)&&(rsum/i)>=Max)
			{
				ans=i;
				break;
			}
//			ll Ned=i*now;
//			if(Ned<=rsum)
//			{
//			}
//			if((i-1)*now<sum)//ÕâÖÖʱºò²ÅÐèÒªÔö¼Ó¿¨×éÊý 
//			{
//				ll back=
//			}
		}
		cout<<ans<<endl; 
	}
	return 0;
}
/*
1
5 4
2 6 1 2 4

*/

D比C简单很多,我开始看题到写完就花了20分钟image-20240928085450057
先考虑找到一个解,发现直接给所有城市按照ddl排序,然后从小的开始向大的覆盖,如果这样的操作是无解的,可以证明一定无解。
然后根据这个,可以推导出一个很重要的贪心,就是最后有解的城市一定是一个完整的答案区间。也就是能够作为起点的城市一定是一个连续的区间。
证明的话,根据我们判断是否有解的贪心可以感性理解,正确性还是挺明显的。
然后根据这个,先以ddl最小的点开始作为起点,向左右二分这个答案区间的边界,就能够找到答案。我写的是倍增,二分一样的。总复杂度\(O(n\log n)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int a=0,b=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
	return a*b;
}
int n,b[200001];
struct city
{
	int id,ddl;
}a[200001];
bool cmp(city a,city b)
{
	return a.ddl<b.ddl;
}
int main()
{
	int T=read();
	while(T--)
	{
		n=read();
		for(int i=1;i<=n;i++)
		{
			b[i]=a[i].ddl=read();
			a[i].id=i;
		}
		sort(a+1,a+1+n,cmp);
		int st=a[1].id;
		int now=1,l,r;l=r=a[1].id;
		for(int i=2;i<=n;i++)
		{
			if(a[i].id<l)
			{
				now+=l-a[i].id;
				if(now>a[i].ddl)break;
				l=a[i].id;
			}
			if(a[i].id>r)
			{
				now+=a[i].id-r;
				if(now>a[i].ddl)break;
				r=a[i].id;
			}
		}
		if(!(l==1&&r==n))
		{
			cout<<0<<endl;
			continue;
		}
		int Maxl=st,Maxr=st;
		int nowm=19;
		while(nowm>=0)
		{
			if(Maxl-(1<<nowm)<=0)
			{
				nowm--;
				continue;
			}
			l=r=a[1].id;
			now=1;l=r=Maxl-(1<<nowm);
			for(int i=1;i<=n;i++)
			{
				if(a[i].id<l)
				{
					now+=l-a[i].id;
					if(now>a[i].ddl)break;
					l=a[i].id;
				}
				if(a[i].id>r)
				{
					now+=a[i].id-r;
					if(now>a[i].ddl)break;
					r=a[i].id;
				}
			}
			if((l==1&&r==n))Maxl-=(1<<nowm);
			nowm--;
		}
		nowm=19;
		while(nowm>=0)
		{
			if(Maxr+(1<<nowm)>n)
			{
				nowm--;
				continue;
			}
			l=r=a[1].id;
			now=1;l=r=Maxr+(1<<nowm);
			for(int i=1;i<=n;i++)
			{
				if(a[i].id<l)
				{
					now+=l-a[i].id;
					if(now>a[i].ddl)break;
					l=a[i].id;
				}
				if(a[i].id>r)
				{
					now+=a[i].id-r;
					if(now>a[i].ddl)break;
					r=a[i].id;
				}
			}
			if((l==1&&r==n))Maxr+=(1<<nowm);
			nowm--;
		}
		cout<<Maxr-Maxl+1<<endl;
	}
	return 0;
}

E题,我的做法很暴力,就是把所有点按照深度排序,然后每次就把相同深度的点和根节电拿出来建立虚树,然后计算这个虚数的边数,用\(n-1\)减去这个数字就是这个深度的答案。计算边数可以用lca,具体参考"异象石"这题。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int a=0,b=1;char c=getchar();
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
	return a*b;
}
int n,head[1000001],tot,Size[500001],dep[500001][22],f[500001][22],dfn[500001],cnt;
struct edge
{
	int next,to;
}e[1000001];
inline void add(int i,int j)
{
	e[++tot].next=head[i];
	e[tot].to=j;
	head[i]=tot;
}
void pre(int x,int fa)
{
	Size[x]=1;f[x][0]=fa;dep[x][0]=dep[fa][0]+1;dfn[x]=++cnt;
	for(int i=head[x];i!=0;i=e[i].next)
	{
		int u=e[i].to;
		if(u==fa)continue;
		pre(u,x);
		Size[x]+=Size[u];
	}
}
int Lca(int x,int y)
{
	if(dep[x][0]<dep[y][0])swap(x,y);
	int now=21;
	while(now>=0)
	{
		if(dep[x][now]>dep[y][0])x=f[x][now];
		now--;
	}
	if(x==y)return x;
	now=21;
	while(now>=0)
	{
		if(f[x][now]!=f[y][now])x=f[x][now],y=f[y][now];
		now--;
	}
	return f[x][0];
}
inline Count(int x,int y)
{
	int lca=Lca(x,y);
	return dep[x][0]+dep[y][0]-dep[lca][0]*2;
}
vector<int> fdp[500001];
bool cmp(int a,int b)
{
	return dfn[a]<dfn[b];
}
int main()
{
	int T=read();
	while(T--)
	{
		for(int i=1;i<=tot;i++)head[i]=0;
		tot=0;
		n=read();
		for(int i=1;i<n;i++)
		{
			int x=read(),y=read();
			add(x,y);add(y,x);
		}
		cnt=0;
		dep[0][0]=-1;
		pre(1,0);
		for(int j=0;j<21;j++)
		{
			for(int i=1;i<=n;i++)
			{
				f[i][j+1]=f[f[i][j]][j];
				dep[i][j+1]=dep[f[i][j]][j];
			}
		}
		for(int i=0;i<=n;i++)fdp[i].clear();
		for(int i=0;i<=n;i++)
		{
			fdp[dep[i][0]].push_back(i);
		}
		int ans=0;
		for(int i=0;i<=n;i++)
		{
			if(fdp[i].size()==0)continue;
			sort(fdp[i].begin(),fdp[i].end(),cmp);
			int sum=0,Alllca=Lca(fdp[i][0],fdp[i][fdp[i].size()-1]);
			for(int j=0;j<fdp[i].size()-1;j++)
			{
				sum+=Count(fdp[i][j],fdp[i][j+1]);
				Alllca=Lca(Alllca,fdp[i][j]);
			}
			sum+=Count(fdp[i][0],fdp[i][fdp[i].size()-1]);
			sum/=2;
			sum+=dep[Alllca][0];
//			cout<<i<<' '<<sum<<' '<<Alllca<<' '<<dep[Alllca][0]<<endl;
			ans=max(ans,sum);
		}
		cout<<(n-1)-ans<<endl;
	}
	return 0;
}

posted @ 2024-09-28 22:35  HL_ZZP  阅读(19)  评论(0编辑  收藏  举报