比赛-2015.01.03

又是一次gdkoi的学校选拔的比赛。

伤心透了。 满分:400   分数:132

题目如下:

<span style="font-family:Microsoft YaHei;font-size:14px;">中位数
【题目描述】
   给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
【输入格式】
    第一行为两个正整数n和b ,第二行为1~n 的排列。
【输出格式】
    输出一个整数,即中位数为b的连续子序列个数。
【样例】
输入	输出
5 4
1 2 3 4 5	2
6 3
1 2 4 5 6 3	1
7 4
5 7 2 4 3 1 6	4

第三个样例解释:{4}, {7,2,4}, {5,7,2,4,3}和{5,7,2,4,3,1,6}。
【数据规模】
编号	1	2	3	4	5	6	7	8	9	10
N	10	50	100	300	1000	3600	10000	25000	55555	100000</span>


对于这道题,先打了个暴力,再是打了个优化往两边搜,但是还是果断超时了几个点

其实这道题就是向左连续的找,话很难说明把,直接打比方

例如第三个样例


左边就是 1 0 -1 0 大就加,小就减

所以数组就是 {...1,2,1} 就是记录个数

之后右边找找 0,-1,-2,-1

结果发现 右边的-1对应 左边的1

              右边的0对应 左边的0

所以就是1+1+2=4。

具体为什么,想想就知道啦。

Code:

<span style="font-family:SimSun;font-size:10px;">#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,b,a[110000],p;
void Input()
{
  scanf("%d%d",&n,&b);
  for(int i=1;i<=n;i++)
  {
    scanf("%d",&a[i]);
	if(a[i]==b) p=i;
  }
}
int fl[210000],fr[210000];
int ans=0;
void Solve()
{
  int w=0;
  for(int i=p;i>=1;i--)
  {
    if(a[i]>a[p]) w--;
	if(a[i]<a[p]) w++;
	fl[w+p]++;
  }
  w=0;
  for(int i=p;i<=n;i++)
  {
    if(a[i]>a[p]) w++;
	if(a[i]<a[p]) w--;
	fr[w+p]++;
  }
  for(int i=1;i<=n;i++)
    ans+=fr[i]*fl[i];
}
void Output()
{
  printf("%d\n",ans);
}
int main()
{  
  freopen("median.in","r",stdin);
  freopen("median.out","w",stdout);
  Input();
  Solve();
  Output();
  return 0;
}</span><span style="font-family:Microsoft YaHei;font-size:14px;">
</span>


<span style="font-family:Microsoft YaHei;font-size:14px;">叶子的颜色
【题目描述】
   给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。
   对于每个叶结点u,定义c[u]为从u到根结点的简单路径上第一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
【输入格式】
    第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。
【输出格式】
    仅一个数,即着色结点数的最小值。
【样例】
输入	输出
5 3
0
1
0
1 4
2 5
4 5
3 5	2
【数据规模】
数据	1	2	3	4	5	6	7	8	9	10
m	10	50	100	200	400	1000	4000	8000	10000	10000
n	5	23	50	98	197	498	2044	4004	5021	4996
</span>

这道题在考场上,连题目都没看。

题目现在我也不讲了,你们看看把。

解法就是tree dp


首先就是b数组的定义。

b[0]就是我的孩子和孙子等等有多少个需要我变为0的。

b[1]同上。

b[2]就是我的孩子是中间派的,就是变0和变1都很随和的。

s就是我的孩子的总数

<span style="font-family:SimSun;font-size:10px;">if(b[0]>b[1]) {col[x]=0; s=s-b[0]-b[2]+1;}
  else if(b[1]>b[0]) {col[x]=1; s=s-b[1]-b[2]+1;}
  else {col[x]=2; s=s-b[1]-b[2]+1;}</span>

很好理解吧其实,col[]就是我变成什么颜色咯。

就下来我都不多罗嗦了,直接递归寻找s咯

Code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int col[110000];
struct node
{
  int x,y,next;
}a[210000]; int len,first[110000];
void ins(int x,int y)
{
  len++; a[len].x=x; a[len].y=y;
  a[len].next=first[x]; first[x]=len;
}
void Input()
{
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++)
    scanf("%d",&col[i]);
  len=0; memset(first,0,sizeof(first));
  for(int i=1;i<n;i++)
  {
    int x,y;
	scanf("%d%d",&x,&y);
	ins(x,y); ins(y,x);
  }
}
int ans;
int Tree_Dp(int x,int fa)
{
  if(x<=m) return 1;
  int b[3]; memset(b,0,sizeof(b));
  int s=0;
  for(int k=first[x];k;k=a[k].next)
  {
    int y=a[k].y;
	if(y!=fa)
	{
	  int t=Tree_Dp(y,x);
	  s+=t;
	  b[col[y]]++;
	}
  }
  if(b[0]>b[1]) {col[x]=0; s=s-b[0]-b[2]+1;}
  else if(b[1]>b[0]) {col[x]=1; s=s-b[1]-b[2]+1;}
  else {col[x]=2; s=s-b[1]-b[2]+1;}
  return s;
}
void Solve()
{
  int sum; ans=m+1;
  sum=Tree_Dp(n,0);
  ans=min(ans,sum);
}
void Output()
{
  printf("%d\n",ans);
}
int main()
{
  freopen("color.in","r",stdin);
  freopen("color.out","w",stdout);
  Input();
  Solve();
  Output();
  return 0;
}


<span style="font-family:Microsoft YaHei;font-size:14px;">循环赛
【题目描述】
   n支队伍打比赛,每两支队伍恰好比赛一场。平局时各得1分,而有胜负时胜者3分,负者0分。
   假设三支队伍得分分别为3, 3, 3,则可能有两种情况: 
队伍	A	B	C	得分
A	-	3	0	3
B	0	-	3	3
C	3	0	-	3

队伍	A	B	C	得分
A	-	0	3	3
B	3	-	0	3
C	0	3	-	3

    给出n支队伍的最终得分(即所有比赛均已结束),统计有多少种可能的分数表。
【输入格式】
    第一行包含一个正整数n,队伍的个数。第二行包含n个非负整数,即每支队伍的得分。
【输出格式】
    输出仅一行,即可能的分数表数目。保证至少存在一个可能的分数表。
【样例】
输入	输出
3
3 3 3	2
2
0 3	1
3
4 1 2	1
6
5 6 7 7 8 8	121
【数据规模】
数据	1	2~3	4~6	7~12	13~19	20~25
n	3	4	5	6	7	8
</span>

这道题就是简单爆搜,加一个剪枝。

首先算出胜场和平场的数目。

之后就是直接搜咯,但貌似正解不是这样的,谁想到可以留言。 时间也很长,过不了全部点,但貌似已经尽最大能力了。

Code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,point[30];
void Input()
{
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
    scanf("%d",&point[i]);
}
int wi,pi;
int a[30],sum;
void dfs(int x,int y,int r1,int r2)
{
  if(x==n)
  {
    if(point[x]==a[x])
	{
	  sum++;
	  return ;
	}
  }
  else if(y==n+1)
  {
    if(point[x]==a[x])
	{
      dfs(x+1,x+2,r1,r2);
	}
  }
  else
  {
    if(r1)
	{
	  a[x]+=3;
	  dfs(x,y+1,r1-1,r2);
	  a[x]-=3;
	  
	  a[y]+=3;
	  dfs(x,y+1,r1-1,r2);
	  a[y]-=3;
	}
	if(r2)
	{
	  a[x]+=1; a[y]+=1;
	  dfs(x,y+1,r1,r2-1);
	  a[x]-=1; a[y]-=1;
	}
  }
}
void Solve()
{
  int ans=0; sum=0;
  for(int i=1;i<=n;i++)
    ans+=point[i];
  wi=ans-(n*(n-1));
  pi=wi-(n*(n-1)/2);
  dfs(1,2,wi,pi);
}
void Output()
{
  printf("%d\n",sum);
}
int main()
{
  freopen("match.in","r",stdin);
  freopen("match.out","w",stdout);
  Input();
  Solve();
  Output();
  return 0;
}


<span style="font-family:Microsoft YaHei;">圣诞树
【题目描述】
 圣诞节到了,FireDancer准备做一棵大圣诞树。左图为圣诞树的一个简单结构。
这棵树被表示成一组被编号的结点和一些边的集合。结点从1到n编号。树的根永远是1。每个结点都有一个自身特有的数值,称为它的重。各个结点的重可能不同。对于一棵做完的树来说,每条边都有一个价值,若设这条边e连接结点i和结点j,且i为j的父结点(根是最老的祖先),则该边的价值为(j的所有子孙及它自己的重之和)*(e的单位价值ce)。
  现在FireDancer想造一棵树,使得树上所有边的总价值最小,并且所有的点都在树上,因为FireDancer喜欢大树。
【输入格式】
  第一行两个整数n和m(0<=n,m<=50000),表示结点总数和可供选择的边数。
  下面一行有n个整数,依次表示每个结点的重。
  下面m行,每行有3个正整数a,b,c,表示结点a和结点b之间有一个单位价值为c的边可供你造树时选择。
  输入中的所有数都小于216。
【输出格式】
  若无解,输出“No Answer”,否则一个整数表示造树的最小价值。
样例输入	输出样例
  2 1
  1 1
  1 2 15	  15

  7 7
  200 10 20 30 40 50 60
  1 2 1
  2 3 3
  2 4 2
  3 5 4
  3 7 2
  3 6 3
  1 5 9
	  1210</span>

没图不好意思了。

其实就可以简单等价成最短路,自己画个图就知道了。

把根节点和其他的边最短路

要离开机房了。 要用longlong

Code:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL unsigned long long
using namespace std;

LL val[51000];
struct node
{
  LL x,y,d,next;
}a[110000]; LL len,first[51000];
LL n,m;
void ins(LL x,LL y,LL d)
{
  len++;
  a[len].x=x; a[len].y=y; a[len].d=d;
  a[len].next=first[x]; first[x]=len;
}
void Input()
{
  scanf("%I64d%I64d",&n,&m);
  for(LL i=1;i<=n;i++)
    scanf("%I64d",&val[i]);
  len=0; memset(first,0,sizeof(first));
  for(LL i=1;i<=m;i++)
  {
    LL x,y,d;
	scanf("%I64d%I64d%I64d",&x,&y,&d);
	ins(x,y,d); ins(y,x,d);
  }
}
queue<LL>Q;
LL d[51000];
bool v[51000];
LL ans=0;
void Solve()
{
  Q.push(1); memset(d,126,sizeof(d));
  d[1]=0; memset(v,0,sizeof(v));
  v[1]=1;
  while(!Q.empty())
  {
    LL x=Q.front();
	for(LL k=first[x];k;k=a[k].next)
	{
	  LL y=a[k].y;
	  if(d[y]>d[x]+a[k].d)
	  {
	    d[y]=d[x]+a[k].d;
		if(!v[y])
		{
		  v[y]=true;
		  Q.push(y);
		}
	  }
	}
	v[x]=false;
	Q.pop();
  }
  for(LL i=2;i<=n;i++)
  {
    ans+=val[i]*d[i];
  }
  printf("%I64d\n",ans);
}
int main()
{ 
  freopen("chris.in","r",stdin);
  freopen("chris.out","w",stdout);
  Input();
  Solve();
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值