Codeforces Round #614 (Div. 2)A--E

A. ConneR and the A.R.C. Markland-N

map+暴力枚举即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
map<int,int>mp;
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int t;
  	cin>>t;
  	while(t--)
  	{
  		int n,s,k,x;
  		cin>>n>>s>>k;
  		mp.clear();
  		for(int i=1;i<=k;i++)
  		cin>>x,mp[x]=1;
  		int mi=111111;
  		for(int i=s;i<=n;i++)if(mp.find(i)==mp.end())
		  {
  			mi=min(mi,i-s);
			  break;
		}
		for(int i=s;i>=1;i--)
		if(mp.find(i)==mp.end())
		{
			mi=min(s-i,mi);
			break;
		}
		cout<<mi<<endl;
	  }
	return 0;
}

B:最优解肯定是一个一个的减少。

因为 2/n < 1/n+1/(n-1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/

int main()
{
  	int n;
  	cin>>n;
  	double ans=0;
  	for(double i=1.0;i<=n;i+=1)
  	{
		ans+=1/i;  		
	}
	printf("%.10f\n",ans);
	return 0;
}

C:

由于只有两行,每次火山产生时只会影响另一行相近的三格。

记录产生的障碍数,等于0则可以通过

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
int vs[3][M];
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n,q;
  	cin>>n>>q;
  	ll nm=0;
  	while(q--)
  	{
  		int r,c;
  		cin>>r>>c;
  		r--;
  		vs[r][c]^=1;
  		if(vs[r][c])//产生岩浆 
  		{
  			if(vs[r^1][c-1])nm++;
  			if(vs[r^1][c])nm++;
  			if(vs[r^1][c+1])nm++;
		}
  		else
		{
			if(vs[r^1][c-1])nm--;
  			if(vs[r^1][c])nm--;
  			if(vs[r^1][c+1])nm--;
		} 
  		if(nm)cout<<"No"<<endl;
  		else cout<<"Yes"<<endl;
	}
	return 0;
}

D:

一看到倍数增长,很自然想到点数不会太多。

而且每次倍数增长,最优解肯定是收取连续的数据点。否则把中间的收取肯定更优

枚举最终收集的连续的点。

然后从起点到端点再收集完这条链上的点一定是最优走法,因为三角形两边之和大于第三边。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
struct node
{
	ll x,y;
}P[M];
ll dis(node a,node b)
{
	ll x,y;
	if(a.x>b.x)x=a.x-b.x;else x=b.x-a.x;
	if(a.y>b.y)y=a.y-b.y;else y=b.y-a.y;
	return x+y;
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	ll ax,ay,bx,by,sx,sy,t;
  	cin>>P[0].x>>P[0].y>>ax>>ay>>bx>>by;
  	cin>>sx>>sy>>t;
  	int sz=0;
	while(1)
	{
		if((1LL*3e16-bx)/ax<P[sz].x||(1LL*3e16-by)/ay<P[sz].y)break;
		P[++sz].x=ax*P[sz-1].x+bx;
		P[sz].y=ay*P[sz-1].y+by;
	//	cout<<sz<<"   "<<P[sz].x<<"  "<<P[sz].y<<"  "<<endl;
	}
//	cout<<sz<<endl;
	int ma=0;
	for(int i=0;i<=sz;i++)
	for(int j=i;j<=sz;j++)
	{
		ll tp=0;
		for(int k=i;k<j;k++)//i ->j 连续的点经过  
		{
			tp+=dis(P[k],P[k+1]);
		}
		node s=node{sx,sy};
		tp+=min(dis(s,P[i]),dis(s,P[j]));
	//	cout<<i<<"  "<<j<<"  "<<tp<<endl;
		if(tp<=t)ma=max(ma,j-i+1);
	}
	cout<<ma<<endl;
	return 0;
}
/*
1 1 2 2 0 0 
1 1 1 
*/

E:

 

mex(u,v)  点u--点v的路径上所有边权,没有出现的最小自然数。

这种非常奇怪的条件非常不好维护,我们可以从贡献着想。

对于边权0一条边u-v,它产生的贡献即sizu*sizv,左右子树乘积。

而对于[0,1],1肯定要放在0旁边,不然拉长后siz左右会变小,不优。

此时额外产生的贡献是sizi*sizj。因为之前0已经算过一遍了。

同理拓展到[0,2].肯定放在[0,1]的旁边形成一条链。额外贡献同理。

我们发现:最终产生贡献的边权[0.k],一定在一条链上,除了这条链上的其他边不会产生贡献,因为没有意义。

假设一条链i,j是最终的链,它可以由长链 i,fa[i][j] 或者长链 fa[j][i],j 转移过来。

 

也就是说,如果这不是一棵树,而是一个数组,让我们填0--k的数,求S的最大值。

我们可以直接dp[i][j]表示 [i,j] 区间内填  [0,j-i] 数,产生的最大贡献

转移:dp[i][j]=max(dp[i][j-1],dp[i+1][j])+i*(n-j).

最后输出dp[i][j]即可。

然后转化到树上问题,我们直接枚举i,j。然后暴力转移即可。

这看似是个n^4的复杂度,但其实每个dp[i][j]加上记忆话后只会算一次。所以复杂度是n^2

最后预处理出:

fa[i][j]:以i为根j的父亲节点。

siz][j]:以i为根j的子树大小。

方便计算。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back 
const int M =3010;
vector<int>G[M];
int f[M][M];//以i为根时 j的父亲。
ll siz[M][M];//以i为根时,j的子树大小
ll dp[M][M];//以i,j为最终算贡献的链时,最大贡献是多少
//最终最大的贡献一定是一条链,链上边权为[0,k]。 
void dfs(int rt,int u,int fa)
{
	//cout<<u<<" "<<fa<<endl;
	siz[rt][u]=1;
	for(auto v:G[u])
	{
		if(v==fa)continue;
	//	cout<<u<<" "<<v<<" "<<fa<<endl;
		f[rt][v]=u;
		dfs(rt,v,u);
		siz[rt][u]+=siz[rt][v];
	}
}
ll ans;
ll gao(int i,int j)
{
//	cout<<i<<" "<<j<<endl;
	if(i==j)return 0;
	if(dp[i][j]!=-1)return dp[i][j];
	return dp[i][j]=siz[i][j]*siz[j][i]+max(gao(f[j][i],j),gao(i,f[i][j]));
}
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n,u,v;cin>>n;
	for(int i=1;i<n;i++)cin>>u>>v,G[u].pb(v),G[v].pb(u);
	for(int i=1;i<=n;i++)dfs(i,i,-1);
	memset(dp,-1,sizeof(dp));
//	cout<<"ok"<<endl;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	gao(i,j),ans=max(ans,dp[i][j]);
	cout<<ans<<endl;
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值