Codeforces Round #410 (Div. 2) 解题报告

A:Mike and palindrome

题意就是让你判断一个字符串最多修改一个字符后能不能成为一个回文串。。

所以只要判回文的时候记录一下有多少个不同的位置就行了

B:Mike and strings

不知道可不可以贪心,题意是给你n个字符串,然后你每次操作可以把一个字符串的第一个字符放到最后面,问最少多少次能让所有字符串一样

。。。反正直接枚举一个标准字符串, 然后模拟就行了

C:Mike and gcd problem

这题还是比较有意思的……

题意:给定一个序列A,每次操作可以选出其中两个数字x,y,删除,并且加入x+y,x-y两个数字,问最少多少次操作让序列的gcd>1

很显然的是,两个新加入的数字的gcd若不是1则必然是偶数,所以我们就贪心的认为尽可能把序列中所有的数字变成偶数……

两个相邻的数如果都是奇数,那么需要一次操作变成两个偶数,若一奇一偶,则需要两次,所以我们先解决两个奇数的情况,再去考虑两个偶数的情况

对了,一开始要先判一下初始序列的gcd是否大于1。。。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <string>
#include <map>
#include <cstring>
#include <ctime>
#include <vector>
#define inf 1e9
#define ll long long
#define For(i,j,k) for(int i=j;i<=k;i++)
#define Dow(i,j,k) for(int i=k;i>=j;i--)
using namespace std;
int t,tmp,tot,n,ans;
int a[5000001];
inline int  gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int main()
{
	scanf("%d",&n);
	scanf("%d",&a[1]),tmp=a[1];
	For(i,2,n)	
	{
		scanf("%d",&a[i]);
		if(a[i]&1)	tot++;tmp=gcd(a[i],tmp);
	}
	if(tmp>1)	
	{
		puts("YES");
		puts("0");return 0;
	}
	For(i,1,n)	a[i]=a[i]&1;
	puts("YES");
	For(i,2,n)
		if(a[i]&&a[i-1])
		{
			a[i]=a[i-1]=0;
			ans++;
		}
	For(i,2,n)	
		if(a[i]^a[i-1])
		{
			a[i]=a[i-1]=0;
			ans+=2;
		}
	printf("%d",ans);
}
D: Mike and distribution

题意:给定两个长度为n的序列A,B,选择一些下标集合T,使2*sigma(A[i]) i∈T >sigma(A) &&2*sigma(B[i]) i∈ T>sigma(B) size_T<=n/2+1;

第一反应……排个序就好了?但是显然不一定最优

那显然取的越多越好,我们就去n/2+1个数字好了

那么怎么分配呢

其实我们可以认为  只要选择的数字和大于未选择的数字和就可以了


所以我们按A排序后,先选择第一个,然后在2、3两个中选择B较大的一个,这样第一个的A一定未大于选择的A,而B较大的一个已经被选,同理,以当前选择的这个数字为“1”,往后推即可

如果是n为偶数……记得把最后一个加上去

当然不加也没关系

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <string>
#include <map>
#include <cstring>
#include <ctime>
#include <vector>
#define inf 1e9
#define ll long long
#define For(i,j,k) for(int i=j;i<=k;i++)
#define Dow(i,j,k) for(int i=k;i>=j;i--)
using namespace std;
int t,tmp,tot,n,ans;
struct node
{
	int x,y,num;
}a[300001];
int ANS[300001];
inline bool cmp(node x,node y)
{
	return x.x>y.x;
}
int main()
{
	scanf("%d",&n);
	For(i,1,n)	scanf("%d",&a[i].x),a[i].num=i;
	For(i,1,n)	scanf("%d",&a[i].y);
	sort(a+1,a+n+1,cmp);
	ans=n/2+1;
	printf("%d\n",ans);
	ANS[++tot]=a[1].num;
	For(i,1,n/2)
	{
		if(i*2+1>n)	break;
		int t1=i*2,t2=i*2+1;
		if(a[t1].y>a[t2].y)	ANS[++tot]=a[t1].num;else ANS[++tot]=a[t2].num;
	}
	if(n%2==0)	ANS[++tot]=a[n].num;
	For(i,1,tot)	printf("%d ",ANS[i]);
}

E: Mike and code of a permutation

这题好难啊OTZ

题意我拷别人博客了。。

题意:

排列p,编码了一个序列a。对于每个i,找到第一个 P

j
pj>piPpi
     pj>pi
并且未被标记的j,标记这个j并 a[i]=j a[i]=j。给出a求一个可行的p,保证有解。 n500000


显然可以得到的大小关系在题目中是 p[a[i]]>p[i]

接下来我们考虑间接得到的大小关系:

定义ys[i]表示i这个数字被几号选中,如a[4]=2则ys[2]=4

对于i,如果j∈[1,a[i]-1]且未被选中过,那么p[j]一定大于p[i],否则a[i]=j,即若ys[j]>i,则p[j]>p[i]

这个很显然

根据这两个大小关系我们就可以构建一个有向图,接下来我们只需要一次dfs的拓扑排序就可以了


然后。。数据范围显然不资磁我们n^2去判第二个大小关系


那怎么办。。。

仔细看这个条件……和区间有关。那么线段树套上去啊,就可以做到n*lg级别了

还有,对于-1,我们可以认为是大于之后所有的数,记为n+1


同理,ys初始值为n+1


#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <string>
#include <map>
#include <cstring>
#include <ctime>
#include <vector>
#define inf 1e9
#define mk make_pair
#define pa pair<int,int>
#define maxn 1000501
#define ll long long
#define For(i,j,k) for(int i=j;i<=k;i++)
#define Dow(i,j,k) for(int i=k;i>=j;i--)
using namespace std;
bool vis[maxn];
int n,ys[maxn],R[maxn*4],L[maxn*4],lson[maxn*4],rson[maxn*4],tim[maxn],T,rt,tot,a[maxn],ans[maxn];
pa ed[maxn*4],tmp;
inline void build(int &x,int l,int r)
{
	x=++tot;L[x]=l;R[x]=r;
	if(l==r)	
	{
		ed[x]=mk(ys[l],l);
		return;
	}
	int mid=(l+r)>>1;
	build(lson[x],l,mid);build(rson[x],mid+1,r);
	ed[x]=max(ed[lson[x]],ed[rson[x]]);
}
inline void query(int x,int l,int r)
{
	if(l<=L[x]&&R[x]<=r)
	{
		tmp=max(tmp,ed[x]);
		return;
	}
	if(l<=R[lson[x]])	query(lson[x],l,r);
	if(r>=L[rson[x]])	query(rson[x],l,r);
}
inline void del(int x,int to)
{
	if(L[x]==R[x])
	{
		ed[x]=mk(0,1);
		return;
	}
	if(to<=R[lson[x]])	del(lson[x],to);else del(rson[x],to);
	ed[x]=max(ed[lson[x]],ed[rson[x]]);
}
inline void dfs(int x)
{
	del(1,x);
	vis[x]=1;	
	if(ys[x]!=n+1)	if(!vis[ys[x]])	dfs(ys[x]);
	if(a[x]>1)
	while(1)
	{
		tmp=mk(0,0);
		query(1,1,a[x]-1);	
		if(tmp.first>x)	dfs(tmp.second);else break;
	}
	tim[++T]=x;
}
int main()
{
	scanf("%d",&n);
	For(i,1,n) ys[i]=n+1;
	For(i,1,n)
	{
		scanf("%d",&a[i]);
		if(a[i]!=-1)	ys[a[i]]=i;else a[i]=n+1;
	}
	build(rt,1,n);
	For(i,1,n)	if(!vis[i])	dfs(i);
	For(i,1,n)	ans[tim[i]]=i;
	For(i,1,n)	printf("%d ",ans[i]);
}	


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值