Atcoder abc259题解 (A~G)

A.Growth Record (简单计算)

根据题意列式,代值进入计算即可。

 cin>>n>>m>>x>>t>>d;
    if(m>=x)
      printf("%d",t);
    else
      printf("%d",t-d*(x-m));

B.Counterclockwise Rotation (简单几何)

题目大意:求(a,b)逆时针旋转d度后的坐标。
利用c++提供的函数直接计算即可。
我们用acos(-1)作为圆周率派的精确值,用hypot(a,b)求斜边长,用atan2(b,a)求边与x轴夹角,就可以轻松求出答案了。

#include
#define ll long long
//const int N=1e6+5;
//const int N=1e5+5;
//const int mod=1e9+7;
//const long long mod=998244353;
const double pi=acos(-1.0);
using namespace std;

int n,t;
double a,b,d,x,y,l,theta;

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin>>a>>b>>d;
    theta=atan2(b,a);
    theta+=d*acos(-1)/180;
    l=hypot(a,b);
    x=l*cos(theta);
    y=l*sin(theta);
    printf("%.20f %.20f",x,y);
}

C. XX to XXX (字符串模拟)

题目大意:给定一个字符串,问这个字符串a能否通过操作变成另外一个字符串b,这个字符串a如果有两个相同字母相连,就可以生成另外一个相同的字母。
用vector上的pair来分别保存两个字符串的信息,相当于将这两个字符串离散成只保存字母和个数信息。接着按照规则匹配即可,大概就是:
1.两个离散后字母数量要相等。
2.如果某个位置上字母b比a要多,那么a这个位置上的字母要大于1个。
3.不允许a的字母比b的多,因为字符串b并不能进行相同操作。

#include
#define ll long long
//const int N=1e6+5;
const int N=2e5+5;
//const int mod=1e9+7;
//const long long mod=998244353;
using namespace std;

int n,t,sum;

char a[N],b[N],c;
vector<pair<char,int>>u,v;

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin>>a+1;
    cin>>b+1;
    int lena=strlen(a+1);
    int lenb=strlen(b+1);
    for(int i=1;i<=lena;++i)
      {
      	if(i==lena)
      	  u.push_back({a[i],1});
      	if(a[i]!=a[i+1])
      	  u.push_back({a[i],1});
      	else
      	  {
      	  	c=a[i];
      	  	sum=0;
      	    while(a[i]==c)
      	      {
      	      	++i;
      	      	++sum;
      	      }
      	    --i;
      	    u.push_back({c,sum});
      	  }
      }
    for(int i=1;i<=lenb;++i)
      {
      	if(i==lenb)
      	  v.push_back({b[i],1});
      	if(b[i]!=b[i+1])
      	  v.push_back({b[i],1});
      	else
      	  {
      	  	c=b[i];
      	  	sum=0;
      	    while(b[i]==c)
      	      {
      	      	++i;
      	      	++sum;
      	      }
      	    --i;
      	    v.push_back({c,sum});
      	  }
      } 
    //printf("%d %d\n",v.size(),u.size());
    if(v.size()!=u.size())
      {
      	printf("No\n");
      	return 0;
      }
    for(int i=0;i<u.size();++i)
      {
      	if(u[i].first!=v[i].first)
      	  {
      	  	printf("No\n");
      	  	return 0;
      	  }
      	else if(u[i].second>v[i].second||(u[i].second==1&&v[i].second>1))
      	  {
      	  	printf("No\n");
      	  	return 0;
      	  }
      }
    printf("Yes\n");
}   

D.Circumferences (简单几何+图论)

题目大意:只通过给定的各个圆边,问某个圆上的点,是否能到达另外一个圆上的点。
我们通过几何关系检查两个圆之间是否相交,如果相交,则建立连接这两个圆编号的双向边。对于给出的点,我们判断它是否能走到另一个圆,必须满足的条件是这个编号能够通过dfs到达代表另一个点所在圆的编号。

#include
#define ll long long
const int N=1e6+5;
//const int N=1e5+5;
//const int mod=1e9+7;
//const long long mod=998244353;
using namespace std;

int n;
ll tx,ty,sx,sy;
ll x[N],y[N],r[N];
int a,b,vis[N];
vector<int>e[N];

ll dis(ll gx,ll gy,ll dx,ll dy)
{
	return (gx-dx)*(gx-dx)+(gy-dy)*(gy-dy);
}

int dfs(int u)
{	
    vis[u]=1;
    if(u==b)
      return 1;
	for(auto v:e[u])
	  {
        if(vis[v])
          continue;
        if(dfs(v))
          return 1;          	  	
	  }
	return 0;
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin>>n;
    cin>>sx>>sy>>tx>>ty;
    for(int i=1;i<=n;++i)
      cin>>x[i]>>y[i]>>r[i];
    for(int i=1;i<=n;++i)
      for(int j=i+1;j<=n;++j)
        {
          if(dis(x[i],y[i],x[j],y[j])>(r[i]+r[j])*(r[i]+r[j])||dis(x[i],y[i],x[j],y[j])<(r[i]-r[j])*(r[i]-r[j]))
            continue;
          e[i].push_back(j),e[j].push_back(i);
        }
    for(int i=1;i<=n;++i)
      {
      	if(dis(sx,sy,x[i],y[i])==r[i]*r[i])
      	  a=i;
      	if(dis(tx,ty,x[i],y[i])==r[i]*r[i])
      	 b=i;
      }
    if(dfs(a))
      printf("Yes\n");
    else
      printf("No\n");
}

E.LCM on Whiteboard (数论)

题目大意:给出n个数字的质因数分解形式,问将任意一个数字变成1后,所有数字的最小公倍数的种类数。
很显然,最小公倍数是所有质数最大幂次的乘积,所以我们用pair将信息存下来,对某一个质因子,判断它是否是该质数的最大幂次,以及是否唯一,注意其它无贡献数字也会整体提供一个公倍数。

#include
#define ll long long
const int N=1e6+5;
//const int N=1e5+5;
//const int mod=1e9+7;
//const long long mod=998244353;
using namespace std;

int n,t,m;
int p,e;
map<int,int>mp,cnt;
vector<pair<int,int>>v[N];

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)
      {
      	cin>>m;
      	while(m--)
      	  {
      	  	cin>>p>>e;
      	  	v[i].push_back({p,e});
      	   if(e>mp[p])
      	     {
      	   	   mp[p]=e;
      	   	   cnt[p]=1;
      	     }
      	   else if(e==mp[p])
      	     ++cnt[p];
      	}
      }
    int ans=0,flag=0,flag2=0;  
    for(int i=1;i<=n;++i)
      {
      	flag=0;
        for(auto x:v[i])
          {
      	    if(mp[x.first]==x.second&&cnt[x.first]==1)
      	      {
      	        flag=1;
      	        break;
      	      }
      	  }
      	if(flag)
      	  ++ans;
      	else
      	  flag2=1;
        }
     if(flag2)
       ++ans;
    printf("%d",ans);
}

F.Select Edges (树上dp)

题目大意:对于一个无向图,他的点有所连边数的限制,选择一些边,在满足限制的同时使边权尽可能大。
我们用 d p [ x ] [ 0 ] dp[x][0] dp[x][0]表示x选择了小于等于其限制 d [ u ] − 1 d[u]-1 d[u]1条边的权值,用 d p [ i ] [ 1 ] dp[i][1] dp[i][1]表示选择了小于等于其限制的 d [ u ] d[u] d[u]的权值,如果有 d p [ x ] [ 0 ] + w > d p [ x ] [ 0 ] dp[x][0]+w>dp[x][0] dp[x][0]+w>dp[x][0]那么选择这条边是更优的情况,因此 d p [ y ] [ 0 ] + e [ i ] . w − d p [ y ] [ 1 ] dp[y][0]+e[i].w-dp[y][1] dp[y][0]+e[i].wdp[y][1]就是选择这条边多获得的权值。每个节点应该由子节点 d p [ y ] [ 1 ] dp[y][1] dp[y][1]转移而来,如果 d [ x ] = = 0 d[x]==0 d[x]==0那么我们应该使 d p [ x ] [ 0 ] = − i n f dp[x][0]=-inf dp[x][0]=inf。利用以上提到的性质即可完成dp。

#include
#define ll long long
const int N=1e6+5;
const long long inf=1e18;
//const int N=1e5+5;
//const int mod=1e9+7;
//const long long mod=998244353;
using namespace std;

int n,t;
int d[N];
int u,v;
ll w;
int head[N],cnt;
ll dp[N][2];

struct edge{
	int next;
	int to;
	ll w;
}e[N];

void add(int from,int to,int w)
{
	e[++cnt].next=head[from];
	e[cnt].to=to;
	e[cnt].w=w;
	head[from]=cnt;
}

void dfs(int x,int fa)
{
	vector<ll>k;
	for(int i=head[x];i;i=e[i].next)
	  {
	  	int y=e[i].to;
	  	if(y==fa)
	  	  continue;
	  	dfs(y,x);
	  	k.push_back(dp[y][0]+e[i].w-dp[y][1]);
	    dp[x][0]+=dp[y][1];
	    dp[x][1]+=dp[y][1];
	  }
	sort(k.begin(),k.end(),greater<ll>());
	int len=k.size();
	for(int i=0;i<len;++i)
	  {
	  	if(k[i]<0)
	  	  break;
	  	if(i<d[x])
	  	  dp[x][1]+=k[i];
	  	if(i<d[x]-1)
	  	  dp[x][0]+=k[i];
	  }
	if(!d[x])
	  dp[x][0]=-inf;
}

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;++i)
      cin>>d[i];
    for(int i=1;i<n;++i)
      {
      	cin>>u>>v>>w;
      	add(u,v,w);
      	add(v,u,w);
      }
      dfs(1,0);
      printf("%lld",dp[1][1]);
}

G.Grid Card Game (最小割)

题目大意:给出一个数字矩阵,其中一个人可以选择某行,并将该行的数字加起来,另外一个人可以选择某列,同样可以将该列的数字加起来。特别的,如果两个人选择的交集在某一个负数上,就会获得一个无限小的负数。题目让我们求出最大可以获得的数字总和。
首先,如果我们什么都不选,那么有0作为答案,显然,获得无限小负数的方案应该被排除在外。接着,我们先贪心的将所有的正数全部选上求和,再考虑减去的情况。
这时,我们建立最小割模型:
1.将矩阵中的正数作为边权,连i到h+j的点,如果割去的话代表着两个人的选择重复。
2.将行的负数和作为边权,连s到i的点,如果割去代表不选该行。
3.将列的负数和作为边权,连h+j到t的点,如果割去代表不选该列。
4.那么,如何实现不出现无限小负数的情况呢,当负数出现时,连i到h+j的点,权值是无限小,因为我们求的是最小割,所以这条边必定会被割去,从而实现目的。
最后,我们将所有正数的和减去最小割便可得出答案。最小割的实现方式如代码所示。

#include
#define ll long long
//const int N=1e6+5;
const int N=5e4+5;
const ll inf=1e18; 
//const int mod=1e9+7;
//const long long mod=998244353;
using namespace std;

int n,t;
ll a[305][305];
int s;

ll dis[N];
ll w,ans;

struct edge{
	int next;
	int to;
	ll w;
}e[N];
int head[N],cnt=1;

void add(int from,int to,ll w)
{
	e[++cnt].next=head[from];
	e[cnt].to=to;
    e[cnt].w=w;
    head[from]=cnt;
}

bool bfs()
{
	memset(dis,-1,sizeof(dis));
	queue<int>q;
	q.push(s);
	dis[s]=0;
	while(!q.empty())
	  {
	  	int x=q.front();
	  	q.pop();
	  	for(int i=head[x];i;i=e[i].next)
	  	  {
	  	  	int y=e[i].to;
	  	  	if(dis[y]==-1&&e[i].w>0)
	  	  	  {
	  	  	  	dis[y]=dis[x]+1;
	  	  	  	q.push(e[i].to);
	  	  	  }
	  	  }
	  }
	return dis[t]!=-1;
}

ll dfs(int x,ll k)
{
	if(x==t)
	  return k;
	ll flow=0,used=0;  
	for(int i=head[x];i;i=e[i].next)
	  {
	  	int y=e[i].to;
	  	if(dis[y]==dis[x]+1&&e[i].w>0)
	  	  {
	  	  	used=dfs(y,min(k,e[i].w));
	  	  	if(!used)
	  	  	  continue;
	  	  	flow+=used;
	  	  	k-=used;
	  	  	e[i].w-=used;
	  	  	e[i^1].w+=used;
	  	  	if(!k)
			  break; 
	  	  }
	  }
	if(!flow)
	  dis[x]=1;
	return flow;
}

ll c[N],r[N],sum;

int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    int h,w;
    cin>>h>>w;
    s=0,t=h+w+1;
    for(int i=1;i<=h;++i)
      for(int j=1;j<=w;++j)
        cin>>a[i][j];
    for(int i=1;i<=h;++i)
      for(int j=1;j<=w;++j)
        {
          if(a[i][j]>=0)
        	{
        	  add(i,h+j,a[i][j]);
        	  add(h+j,i,0);
        	  ans+=a[i][j];
        	}
          else
            {
              add(i,h+j,-inf);
              add(h+j,i,inf);
              r[i]-=a[i][j];
              c[j]-=a[i][j];
            }
        }
    for(int i=1;i<=h;++i)
      {
      	add(s,i,r[i]);
      	add(i,s,0);
      }
    for(int j=1;j<=w;++j)
      {
      	add(h+j,t,c[j]);
      	add(t,h+j,0);
      }
    while(bfs())
      sum+=dfs(s,inf);
    printf("%lld\n",ans-sum);
}

`

你可能感兴趣的:(Atcoder题解,算法,acm竞赛,c++,数据结构,动态规划)