NKOJ 2019.5.1五一欢乐赛:Round1

A:

在这里插入图片描述在这里插入图片描述

解:

暴力。假设 A , B A,B A,B分别是下标为奇数和偶数的序列。找出每个序列中出现次数最多。
然后用 n n n减去次数之和。
但是题目规定这两个序列的数不能一样,所以需要特判一下。
附上本蒟蒻十分丑陋的代码

#include<bits/stdc++.h>
using namespace std;
int n;
const int maxn=1e5+5;
typedef pair<int,int> P;
#define x first
#define y second
P a1[maxn],a2[maxn];
int main()
{
	cin>>n;
	int a;
	for(int i=1;i<=n;i++)
	{
		cin>>a;
		if(i&1)
		{		
			a1[a].x++;
			a1[a].y=a;
		}
		else
		{
			a2[a].x++;
			a2[a].y=a;
		}
	}
	sort(a1+1,a1+maxn-4,greater<P>() );
	sort(a2+1,a2+maxn-4,greater<P>() );
	if(a1[1].y!=a2[1].y)cout<<n-a1[1].x-a2[1].x;
	else cout<<n-max(a1[2].x+a2[1].x,a1[1].x+a2[2].x);
	return 0;
}

B:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

解:

我们知道使 ∑ i = 1 n ∣ a i − x ∣ \sum_{i=1}^{n}|a_i-x| i=1naix最小的 x x x是序列 { a i } \{a_i\} {ai}的中位数。
这题要求的是 ∑ i = 1 n ∣ a i − ( x + i ) ∣ \sum_{i=1}^n|a_i-(x+i)| i=1nai(x+i)的最小值。
我们立即注意到:
b i = a i − i b_i=a_i-i bi=aii
则原式为:
∑ i = 1 n ∣ b i − x ∣ \sum_{i=1}^n|b_i-x| i=1nbix
所以 x x x为序列 { b i } \{b_i\} {bi}的中位数

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
typedef long long  ll;
ll sum;
ll a[maxn];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		a[i]-=i;
	}
	sort(a+1,a+1+n);
	ll x;
	if(n&1)x=a[n/2+1];
	else x=(a[n/2]+a[n/2+1])/2;
	for(int i=1;i<=n;i++)sum+=abs(a[i]-x);
	cout<<sum;
	return 0;
}

c

在这里插入图片描述 在这里插入图片描述

解:

这道很显然是求对于一个数 x x x,在 [ 1 , x − 1 ] [1,x-1] [1,x1]中有多少数是与它不互质的。
[ 1 , x − 1 ] [1,x-1] [1,x1]中与 x x x互质的数的个数是 φ ( x ) \varphi(x) φ(x)
a n s = x − 1 − φ ( x ) ans=x-1-\varphi(x) ans=x1φ(x)

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=10000000+5;
int phi[maxn];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)phi[i]=i;
	for(int i=2;i<=n;i++)
	{
		if(phi[i]==i)
		{
			for(int j=i;j<=n;j+=i)
			phi[j]=phi[j]/i*(i-1);
		}
	}
	int x;
	for(int i=1;i<=m;i++)
	{
		cin>>x;
		cout<<x-1-phi[x]<<" ";
	}
	return 0;
}

D:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

解:

把箭的颜色当做模板,把怪兽的颜色当做文本
那么第一个问题就是就模板在文本中最大的匹配长度。
很自然地就想到了 K M P KMP KMP
一边匹配一边记录最大的匹配长度。
第二个问题在记录匹配长度的时候就可以用前缀和算出。

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn=20000+5;
char A[maxn],B[maxn];
int fail[maxn];
int w[maxn];
int cnt=0,ans;
void kmp()
{
	int j=0;
	for(int i=2;i<=n;i++)
	{
		while(j&&B[j+1]!=B[i])j=fail[j];
		if(B[j+1]==B[i])j++;
		fail[i]=j;
	}
	j=0;
	for(int i=1;i<=m;i++)
	{
		while(j&&B[j+1]!=A[i])j=fail[j];
		if(B[j+1]==A[i])j++;
		if(j>=cnt)
		{
			if(j==cnt)ans=max(ans,w[i]-w[i-j]);
			else ans=w[i]-w[i-j];
			cnt=j;
		}
	}
}

int main()
{
	scanf("%d %d",&n,&m);
	scanf("%s",&B[1]);
	scanf("%s",&A[1]);
	for(int i=1;i<=m;i++)scanf("%d",&w[i]),w[i]+=w[i-1];
	kmp();
	printf("%d %d",cnt,ans);
}

E :

在这里插入图片描述在这里插入图片描述

解:

这一题可以用图论知识来做。
如果 ∣ S i − S j ∣ &gt; k |S_i-S_j|&gt;k SiSj>k,那么在 i , j i,j i,j之间连一条双向边。
那么最后的答案就是在这个图中,经过所有节点一次的路径有多少条。
考虑一下暴力的 d f s dfs dfs
可能出现极端情况,图是一个无向完全图。
路径条数最大为 16 ! 16! 16!条。显然会 T T T
于是考虑优化一下,用记忆化搜索。
f [ i ] [ s ] f[i][s] f[i][s]表示在 i i i号结点把集合 S S S中的结点走完的路径条数。
显然可以得到转移方程:
f [ i ] [ s ] = ∑ k ∈ S f [ k ] [ S − { k } ] f[i][s]=\sum_{k\in S}f[k][S-\{k\}] f[i][s]=kSf[k][S{k}]

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lowbit(x) (x&-x)
bool g[20][20];
int s[20];
int n,k;
int ans;
ll f[17][1<<16];
int logg[1<<16];
ll dfs(int x,int s)//s中1代表未去过 
{
	if(f[x][s])return f[x][s];
	for(int s1=s,k=lowbit(s1),i=logg[k]+1,s0=s^k;s1;s1^=k,k=lowbit(s1),i=logg[k]+1,s0=s^k)
	if(g[x][i])f[x][s]+=dfs(i,s0);
	return f[x][s];
	
}
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>s[i],g[0][i]=true,logg[1<<i]=i,f[i][0]=1;
	for(int i=1;i<=n;i++)
	for(int j=i+1;j<=n;j++)
	if(abs(s[i]-s[j])>k)g[i][j]=g[j][i]=true;
	cout<<dfs(0,(1<<n)-1);
	return 0;
}

:注意本题数据范围。。。。。。。。。。

F:

在这里插入图片描述在这里插入图片描述在这里插入图片描述

解:

设有三个顾客 x , x 1 , x 2 x,x_1,x_2 x,x1,x2,送客顺序是 x → x 1 → x 2 x\rightarrow x_1\rightarrow x_2 xx1x2
在这里插入图片描述
情况一:
如果 x 1 x_1 x1位于 x x x x 2 x_2 x2之间,那么不送 x 1 x_1 x1的路程和送 x 1 x_1 x1的路程是一样。或者说:从 x x x去送 x 2 x_2 x2的时候顺便把 x 1 x_1 x1送了。
在这里插入图片描述
情况二:
如果 x x x送完 x 1 x_1 x1后要转向,那么不送 x 1 x_1 x1的路程比送 x 1 x_1 x1的路程少 d i s x → x 1 × 2 dis_{x\rightarrow x_1}\times 2 disxx1×2

那么对于最后一顾客,可以在添加一个位于原点的客户。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int a[maxn];
int n,k;
int sum=0;
int w[maxn];
int ans;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{scanf("%d",&a[i]);}
		a[++n]=0;
	for(int i=1;i<=n;i++)ans+=abs(a[i]-a[i-1]);
	for(int i=2;i<=n;i++)
	{
		if((a[i]-a[i-1])*(a[i-1]-a[i-2])<0)w[i-1]=2*min(abs(a[i]-a[i-1]),abs(a[i-1]-a[i-2]));
	}
	for(int i=1;i<=n-1;i++)
	printf("%d\n",ans-w[i]);
	return 0;
	
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值