第七届福建省赛 7/10

题目链接

 

以下博文参考大牛的题解 OTZ

     真的是越发认识到自己菜到爆炸,思维思维跟不上,算法算法也不会.我还会干什么....

 

 

Problem 2262 Best Friend Forever

 

Problem 2263 Bond 

 

Problem 2264 Card Game (First Edition)

 

这是一道很好的数学期望的题目吧。

 

每回合两个人轮流在一个序列中任意取一个数,谁的数大谁得1分 ,相等 不得分,问你所有可能下先手得分的期望

 

能确定得分的期望,那么就是任意顺序下得到的分数,除以所有可能的轮数.

那么2n个数,取数是有顺序的,所以所有可能的轮数就是(2n!)

 

下面我们考虑先手拿到了ai 对结果的影响,

 

那就是 ∑nj=1[ai>aj]×(2n−2)!×C(n,1)

后手取的 是(aj) 固定这两个数 然后一共有n轮,这是其中的一轮,所以乘一个C(n,1) 
而其他的2n−2个数 就又是全排列的

然后约分下式子就是∑ni=1∑nj=1[ai>aj]n∗4−2

计算∑nj=1[ai>aj]的时候只要先对a排个序 然后二分就好了

总复杂度是O(nlogn)

//这里的话我记得是存在这么一个结论,是在去年大连区域赛有个题目,

对于取数的问题,你放回与不放回的概率,是一样的. 

所以这里,我们就把每轮的取数看成第一次的就可以了,这样好理解/.

 

 

 

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
int t,n;
int a[maxn];
int main(){

	scanf("%d",&t);
	for(int k=1;k<=t;k++)
	{
		scanf("%d",&n);
		n*=2;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
		}
		int ans=0;
		sort(a+1,a+1+n);
		for(int i=1;i<=n;i++)
		{
			int tmp=lower_bound(a+1,a+1+n,a[i])-a;
			ans+=tmp-1;
		}
		double ss=ans*1.0/(2*n-2);
		printf("Case %d: %.2lf\n",k,ss);
	}
	return 0;
}



 

 

 

 

 

其实对于这个题目还有更简单的解法,我当时作比赛的时候,我想 

因为两个人取得是同一堆数,每个数又不相同,随机取数,每一局必有输赢 ,

两个人各赢的概率是1/2,最多得n分,那么理论得分就是n/2..

 

我猜了这么一下,竟然真的过了.

 

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
int t,n;
int a;
int main(){

	scanf("%d",&t);
	for(int k=1;k<=t;k++)
	{
		scanf("%d",&n);;
		for(int i=1;i<=2*n;i++)
		{
			scanf("%d",&a);
		}
		printf("Case %d: %.2lf\n",k,n*1.0/2);
	}
	return 0;
}



 

 


Problem 2265 Card Game (Second Edition)

 

 

 

题目和上一个题差不多,只是这次每个人各有n个卡片.

 

还是那个道理,求期望得分就等于任意顺序下的得到的分数除以 总轮数.

只不过不同的各有一堆.

 

公式为 

∑ni=1∑nj=1[ai>bj]×C(n,1)×(n−1)!×(n−1)!n!×n!=∑ni=1∑nj=1[ai>bj]n

 

 

 

 

#include<iostream>
#include<cstdio>
#include<string.h> 
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
int t,n;
int a[maxn],b[maxn];
int main(){

	scanf("%d",&t);
	for(int k=1;k<=t;k++)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
		sort(a+1,a+1+n);
		sort(b+1,b+1+n);
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			int s=lower_bound(b+1,b+1+n,a[i])-b;
			ans+=s-1;
		}
		double ss=ans*1.0/n;
		printf("Case %d: %.2lf\n",k,ss);
	}
	return 0;
}



 

 

 

 

 

Problem 2266 Card Game (Third Edition) 

 

签到题目,直接计算和加起来大于10的有多少

 

Problem 2267 The Bigger the Better

 

 

给你两个序列, 然你合并成一个字符串,保证新字符串中每个元素在原字符串中的顺序不该变

, 使得这个新的字符串的字典序最大。

 

 

思路:

 

按照咱们贪心的思想,要使字典序达到最大,那么肯定是开两个指针每次比较这两个字符串,取字典序最大的放在前面,但是麻烦的是当当前两个字符的大小相等时我们如何确定应该把哪个放在前面.

最简单的方法就是将两个指针一直往后推进比较后面的字符的大小,找到大的放在前面,但是如果这样枚举复杂度为O(n^2) 我们的n,m最大为1e5.肯定不可以的.所以我们要想别的方法,

    在这里可以通过后缀数组中的rank数组来巧妙的解决这个事情,我们将两个字符串合并起来,(但是两个字符串之间需要增加一个对题目没有影响的,又不会影响字典序的字符,这里添加0),即将字符串合并为 (xx..0xxxx...)的形式.然后求一次后缀数组,得到rank数组,这样当当前两个字符串字典序相等时通过比较rank的大小,就可以将后面的比较出大小,从而决定哪个放在前面.

 

 

说一下为什么需要添加一个字符,原本我是觉得没什么影响的,直到队友给我一个样例.有助于大家对后缀数组的理解吧.

比如 74 746 如果不加中间的0.得出的答案就是 77446 ,而实际答案是77464.这是为什么呢?

这是因为当我们以4746为后缀时,和46比较你会发现46应该排在4746的前面,所以当我们枚举合并的时候,自然就成了77446了.可是我们别忘了,这个4 后面的746并不是他自己后面的东西啊,也就是说这样我们的比较就将后面的数给当成可以填的数了,可是我们填过了已经.所以这里加0,巧妙的解决了这件事,因为0永远字典序最小,后面那些无论多大都不会有影响了.

 

 

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
typedef long long int LL;
using namespace std;

const int N   = 4e5+7;
const int MOD = 1e9+7;

int n,m;

const int MAXN=400100;
//以下为倍增算法求后缀数组
int wa[MAXN],wb[MAXN],wv[MAXN],Ws[MAXN];
int sa[MAXN],Rank[MAXN],height[MAXN];
int cmp(int *r,int a,int b,int l) {
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
/**< 传入参数:str,sa,len+1,ASCII_MAX+1 */
void da(const int r[],int sa[],int n,int m) {
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) Ws[i]=0;
    for(i=0; i<n; i++) Ws[x[i]=r[i]]++;//以字符的ascii码为下标
    for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
    for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
    
    for(j=1,p=1; p<n; j*=2,m=p) {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) wv[i]=x[y[i]];
        for(i=0; i<m; i++) Ws[i]=0;
        for(i=0; i<n; i++) Ws[wv[i]]++;
        for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
        for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}
//求height数组
/**< str,sa,len */
void calheight(const int *r,int *sa,int n) {
    int i,j,k=0;
    for(i=1; i<n; i++) Rank[sa[i]]=i;
     for(i=0; i<n; height[Rank[i++]]=k)
        for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);
    // Unified  不要乱用,出来检查为了方便的时候 否则容易RE,WA
//    for(int i=n; i>=1; --i) ++sa[i],Rank[i]=Rank[i-1];
}
int a[N];
int main()
{
	int n,m;
	int t;
	cin>>t;
	int _=0;
	while(t--)
	{
		cin>>n>>m;
		int len=n+m+1;
		for(int i=0;i<n;i++)
		scanf("%d",&a[i]);
		a[n]=0;
		for(int i=0;i<m;i++)
		scanf("%d",&a[i+n+1]);
		da(a,sa,len,101);
		//a[len]=0;
		calheight(a,sa,len);
		int p1=0,p2=n+1;
		printf("Case %d: ",++_);
		while(p1<n&&p2<len)
		{
			if(a[p1]<a[p2])
			printf("%d",a[p2++]);
			else if(a[p1]>a[p2])
			printf("%d",a[p1++]);
			else if(Rank[p1]>Rank[p2])
			printf("%d",a[p1++]);
			else
			printf("%d",a[p2++]);
		}
		while(p1<n)
		printf("%d",a[p1++]);
		while(p2<len)
		printf("%d",a[p2++]);
		puts("");
	}
	return 0;
}

 

 

 

 

 

 

Problem 2268 Cutting Game

 

 

这道题目就比较有意思了, 给你一张n面值的钱,问你要把这个数拆成几个ai元的纸币,

使它能买自己买的起的东西,还不需要找零钱..

 

当时做这个题的时候没太想明白,,就一直再写,写了几个发现首先要有1,这样才能凑奇数偶数.

然后剩余的就拆2 4 8 16 ,这样才能凑出所有的,然后我发现这不就是二进制么,

直接统计n的二进制位数就好了啊.

 

代码没了....fzu不能看比赛的代码..

 

 

Problem 2269 Picking Game

 

 

Problem 2270 Two Triangles

 

 

给你一堆点让你计算能组成几个全等三角形, 

注意 先手拿a 后手拿b 和先手拿b 后手拿a 算两种.
 

我们看到N只有10,所以可以暴力枚举点.

另外 ,题目规定只可以旋转,不能翻转,那么我们就固定一个三角形,然后旋转另一个.

 

如何判断全等呢?三角形三条边对应相等就可以.

 

因为只可以旋转,所以在描述一个三角形是要么都顺时针要么都逆时针.

 

 

所以们要对两个图形判定一下,只要O(3∗3)固定一个旋转另一个判定就好了, 有一次满足就行,

然后n只有10 ,所以C(10,3)=120,所以暴力枚举两个三角形在判定是否一样

所以总复杂度是O(C(10,3)2∗9)

 

 

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<math.h> 
#define exp 0.000000001
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
struct node
{
	int x;
	int y;	
}q[20];
int ans;
int t,n;
int dis(int i,int j)//计算距离
{
	return (q[i].x-q[j].x)*(q[i].x-q[j].x)+(q[i].y-q[j].y)*(q[i].y-q[j].y);
}
int mul(int p0,int p1,int p2)//向量叉乘,判断向量位置
{
	return (q[p2].x-q[p0].x)*(q[p1].y-q[p0].y)-(q[p1].x-q[p0].x)*(q[p2].y-q[p0].y);
}
int check(int s1p0,int s1p1,int s1p2,int s2p0,int s2p1,int s2p2)
{
	if(mul(s1p0,s1p1,s1p2)<0) swap(s1p1,s1p2);//采用顺时针来描述三角形
	if(mul(s2p0,s2p1,s2p2)<0) swap(s2p1,s2p2);
	
	int s11=dis(s1p0,s1p1);
	int s12=dis(s1p1,s1p2);
	int s13=dis(s1p0,s1p2);
	
	int s21=dis(s2p0,s2p1);
	int s22=dis(s2p1,s2p2);
	int s23=dis(s2p0,s2p2);
	//固定一个三角形,旋转另一个三角形,判断是否全等.
	if(s11==s21&&s12==s22&&s13==s23) return 1;
	if(s11==s22&&s12==s23&&s13==s21) return 1;
	if(s11==s23&&s12==s21&&s13==s22) return 1;
	return 0;
}
double len[5];
void solve(int p0,int p1,int p2)
{
	len[1]=sqrt(dis(p0,p1));
	len[2]=sqrt(dis(p1,p2));
	len[3]=sqrt(dis(p0,p2));
	sort(len+1,len+4);//判断能否构成三角形
	if(len[1]+len[2]<=len[3]+exp&&len[1]+len[2]>=len[3]-exp)
	return ;
	for(int i=1;i<=n-2;i++)//再枚举另一个三角形
	{
		if(i==p0||i==p1||i==p2)
		continue;
		for(int j=i+1;j<=n-1;j++)
		{
			if(j==p0||j==p1||j==p2)
			continue;
			for(int k=j+1;k<=n;k++)
			{
				if(k==p0||k==p1||k==p2)
				continue;
				if(check(p0,p1,p2,i,j,k))
				ans++;
			}
		}
	}
}
int main(){

	scanf("%d",&t);
	for(int k=1;k<=t;k++)
	{
		ans=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d %d",&q[i].x,&q[i].y);
		
		for(int i=1;i<=n-2;i++)//先枚举一个三角形
		{
			for(int j=i+1;j<=n-1;j++)
			{
				for(int kk=j+1;kk<=n;kk++)
				{
					solve(i,j,kk);
				}
			}
		}
		printf("Case %d: %d\n",k,ans);
	}
	return 0;
}



 

 

 

Problem 2271 X 

 

 

给你n个点,m条路,让你在保证任意两点最短距离不变的情况下,可以删除多少条路.

 

 

图论对于我来说就是空白...

 

考虑n最多为100,又是任意两点之间的最短路,所以考虑Floyd ...

 

考虑松弛的松弛的情况,

 

 

if(floyd[i][j]>=floyd[i][k]+floyd[k][j])floyd[i][j]=floyd[i][k]+floyd[k][j];

 

那么这个过程中,如果边map[i][j] 被松弛了 那么这条边就可以被删掉了,

 

另外需要考虑去重边和判断这两个点之间原本是否有路.

 

 

#include<iostream>
#include<cstdio>
#include<string.h> 
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
int t; 
int n,m;
int mp[111][111];
int book[111][111];
int flag[111][111];
int main(){
	
	int k=1;
	scanf("%d",&t);
	while(t--)
	{
	scanf("%d %d",&n,&m);
	memset(mp,inf,sizeof(mp));
	memset(flag,0,sizeof(flag));
	memset(book,0,sizeof(book));
	int x,y,z;
	int cnt=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		if(mp[x][y]==inf)
		mp[x][y]=mp[y][x]=z,flag[x][y]=flag[y][x]=1;
		else
		{
			cnt++;
			mp[x][y]=mp[y][x]=min(z,mp[x][y]);
		}
	}
	for(int i=1;i<=n;i++)
	mp[i][i]=0;
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(mp[i][j]>=mp[i][k]+mp[k][j])	
				{
					mp[i][j]=mp[i][k]+mp[k][j];
					if(i!=j&&j!=k&&k!=i&&!book[i][j]&&flag[i][j])
					cnt++,book[i][j]=book[j][i]=1; 
				}
			}
		}
		printf("Case %d: %d\n",k++,cnt);
	}
	return 0;
}



 

 

 

if(floyd[i][j]>=floyd[i][k]+floyd[k][j])floyd[i][j]=floyd[i][k]+floyd[k][j];if(floyd[i][j]>=floyd[i][k]+floyd[k][j])floyd[i][j]=floyd[i][k]+floyd[k][j];

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值