Educational Codeforces Round 49 (Rated for Div. 2) 题解

昨天晚上打的这场有点颓啊,B题因为double的精度问题差点卡全场,C题有个小bug一直没找出来。赛后看了一眼D,比前几道都简单。。。最无语的还是B题了,怎么验数据都对。最后10分钟的时候,抱着破罐破摔的心态,把double改掉再次交上去,神奇AC,接着又找到了C的bug。10分钟内极限AC两题,刺激。。。

 

A题

题目链接:http://codeforces.com/problemset/problem/1027/A

题意:判断是否通过变换可以得到回文串。

 容易想到,从两边开始搜索,如果两个字母一样就不去管它,若不一样,再看一下是否可以变成一样的。。。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[1010];
bool ok(int a,int b)
{
	if(a+1==b-1)
	return true;
	if(a-1==b+1)
	return true;
	return false;
}
int main()
{
	int t;
	int n,len;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		scanf("%s",a+1);
		int flag=1;
		for(int i=1;i<=n;i++)
		{
			if(a[i]==a[n-(i-1)])
			continue;
			else
			{
				if(ok(a[i],a[n-(i-1)]))
				continue;
				else
				{
					flag=0;
					break;
				}
			}
		}
		if(flag==1)
		printf("YES\n");
		else
		printf("NO\n");
	}
	return 0;
} 

B题

题意:按题目中所示规律填数,询问每个格子(x,y)中的数是什么。

 大致思路就是统计每行有几个数,在询问的格子之前有多少行,自己随便推推就行了。在这里我分了两种情况,一种是n为奇数,1种是n为偶数。两种其实差不多,在处理奇数时因为每行的数不一定相同,需要特殊考虑一下,不算太难。

不过请注意!!!在数很大做乘法时不要使用double,昨天因为这个精度问题被卡了很久。。。

代码:(比赛时写的,有点丑,见谅。。。)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
	ll n,q;
	ll x,y;
	ll ans;
	scanf("%lld%lld",&n,&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%lld%lld",&x,&y);
		if(n%2==0)
		{
		if((x+y)%2==0)
		{
		    ans=((n*(x-1))/2+(y+1)/2);	
		}
		else
		{
			ans=((n*n)/2+n*(x-1)/2+(y+1)/2);
		}
	    }
	    else
	    {
	    	if((x+y)%2==0)
		   {
		      if(x%2==1)
			  ans=(n*(x-1))/2+(y+1)/2;
			  else
			  ans=(n*(x-1))/2+(y+1)/2+1;	
		   }
		   else
		   {
			  ans=((n*n+1)/2)+n*(x-1)/2+ceil((y+1)/2);
		    }
		}
		printf("%lld\n",ans);
	}
	return 0;

C题

题意:给你一些木棒,选出其中四根拼成矩形,并要求p*p/s最小(p为周长,s为面积)。

首先如果 要是一个长度的木棒达到四根,显然选这个长度。如果没有的话,将所有出现两次的木棒都加入vector中,从小到大排序。根据均值不等式,b/a最小时为所求解,那么我们就看相邻两根就可以了,从中挑出b/a最小的即可。。。

不过这道题T有1e6组,如果每次都memset,总复杂度是1e10,emmmm。。。可还是能过的。。。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=1e6+10;
int a[maxn];
int cnt[10001];
vector<double> v;  
int past[maxn];
template <class T>
inline bool scan_d(T &ret) 
{
    char c; 
    int sgn;
    if (c = getchar(), c == EOF) 
    {
        return 0; //EOF 
    }
    while (c != '-' && (c < '0' || c > '9')) 
    {
        c = getchar(); 
    }
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0'); 
    while (c = getchar(), c >= '0' && c <= '9') 
    {
        ret = ret * 10 + (c - '0'); 
    }
    ret *= sgn;
    return 1;
}

template <class T>
inline void print_d(T x) 
{ 
    if (x > 9) 
    {
        print_d(x / 10); 
    }
    putchar(x % 10 + '0');
}
int main()
{
	int t;
	int n;
	int r1,r2,len;
	double cha;
	double res;
	scanf("%d",&t);
	while(t--)
	{
		v.clear();
		scan_d(n);
		int flag=0;
		int rec;
//		for(int i=1;i<=10000;i++)
//		{
//			if(cnt[i])
//			cnt[i]=0;
//		}
        memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;i++)
		{
		   scan_d(a[i]);
//           scanf("%d",&a[i]);
		   cnt[a[i]]++;
		   if(cnt[a[i]]==4)
		   {
		   	  flag=1;
		   	  rec=a[i];
		   }
		   else if(cnt[a[i]]==2)
		   v.push_back(a[i]);
	    }
	    if(flag==1)
	    {
	    	 printf("%d %d %d %d\n",rec,rec,rec,rec);
		}
		else
		{
			 sort(v.begin(),v.end());
			 len=v.size();
			 cha=1e6;
			 for(int i=0;i<len-1;i++)
			 {
			 	 res=v[i+1]/v[i];
				  if(res<cha)
			 	 {
			 	     cha=res;
					 r1=v[i];
					 r2=v[i+1];	
				 }
			 }
			 printf("%d %d %d %d\n",r1,r1,r2,r2);
		}
	}
	return 0;
} 

D题

有n个房间,每个房间放捕鼠夹有一个费用,老鼠每一秒从一个房间跑到另一个房间(可以自环),求要抓住老鼠最小花费。

这道题比赛时根本没看 ,后来看了一下,发现是道板子题。

首先Tarjin缩点,这样只要在终点处放老鼠夹就好了。在终点的强连通分量中可能有多个房间,选择费用最小的一个即可。。。

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200010; 
const int MAXM = 200010; 
typedef long long ll;
int a[MAXN];
struct Edge
{
    int to, next;
}edge[MAXM];
int Outdegree[MAXN];
int Indegree[MAXN];
int head[MAXN], tot;
int Low[MAXN], DFN[MAXN], Stack[MAXN], Belong[MAXN];    
int Index, top;
int scc;                                                
bool Instack[MAXN];
int num[MAXN];                                         
vector<int> ans;   
vector<int> Have[MAXN];                                                     
void addedge(int u, int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    return ;
}

void Tarjan(int u)
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[top++] = u;
    Instack[u] = true;
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if (!DFN[v])
        {
            Tarjan(v);
            if (Low[u] > Low[v])
            {
                Low[u] = Low[v];
            }
        }
        else if (Instack[v] && Low[u] > DFN[v])
        {
            Low[u] = DFN[v];
        }
    }
    if (Low[u] == DFN[u])
    {
        scc++;
        do
        {
            v = Stack[--top];
            Instack[v] = false;
            Belong[v] = scc; 
			Have[scc].push_back(v);
			num[scc]++;
        }
        while (v != u);
    }
    return ;
}

ll solve(int N)
{
    memset(DFN, 0, sizeof(DFN));
    memset(Instack, false, sizeof(Instack));
    memset(num, 0, sizeof(num));
    Index = scc = top = 0;
    for (int i = 1; i <= N; i++)
    {
        if (!DFN[i])
        {
            Tarjan(i);
        }
    }
    for(int i=1;i<=N;i++)
    {
		int v;
		for (int j=head[i];j!=-1;j=edge[j].next)
		{
			v=edge[j].to;
			if(Belong[i]!=Belong[v])
			{
				Outdegree[Belong[i]]++;
				Indegree[Belong[v]]++;
			}
		}
    }
    for(int i=1;i<=scc;i++)
    {
    	 if(Outdegree[i]==0)
    	 ans.push_back(i);
	}
	int len=ans.size();
	int len2;
	ll res=0;
	int cur;
	ll tem;
	for(int i=0;i<len;i++)
	{
		 cur=ans[i];
		 len2=Have[cur].size();
		 tem=2e10;
		 for(int j=0;j<len2;j++)
		 {
		 	  if(a[Have[cur][j]]<tem)
		 	  {
		 	  	  tem=a[Have[cur][j]];
			   }
		 }
		 res+=tem;
	}
    return res;
}

void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
    return ;
}

int main()
{
	int n,v;
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&v);
		addedge(i,v);
	}
	cout << solve(n) << endl;
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值