纪中集训 Day19&Day20
T1:
神神奇奇的多打了个=
T2:
纯粹蒙分
T3:
我也不知道发生了什么
T4:
直接蒙分
T1
题目:
经过六年的努力,小明终于被一所知名中学录取。优秀的小明总是对一些奇奇怪怪的事情感兴趣,这次他想知道谁在这所新学校拥有的朋友最多,由于大家都才刚报到,所以小明只知道大家两两之间是否是朋友关系。
输入:
第一行有两个整数
n
n
n和
m
m
m,
n
n
n表示总人数,
m
m
m表示总关系数。
接下来
m
m
m行,每行有2个以空格隔开的整数a和
b
b
b,表示
a
a
a和
b
b
b是朋友,
a
a
a和
b
b
b均为1到
n
n
n之间的整数。不会给出重复的朋友关系。
输出:
仅有一行,表示朋友数最多的人所拥有的朋友,每两个整数之间用空格隔开,按照字典序从小到大输出。如果存在多个人朋友数都是最多的情况,请输出字典序最小的那人的答案,具体见样例。
样例:
input
3 3
1 2
2 3
1 3
output
2 3
数据范围限制:
50%的数据,1 <=
n
n
n <= 10
80%的数据,1 <=
n
n
n <= 1000
100%的数据,1 <=
n
n
n <= 10000,
m
m
m <= 500000
提示:
1、2、3均拥有2个朋友,因此输出字典序较小的1的朋友即可。
1的朋友为2和3,按照字典序从小到大输出,数字之间用空格隔开。
解题思路:
二维数组存关系
一维数组存朋友数
找到最多的朋友数的人且编号最小
把
t
a
ta
ta的朋友排序输出
代码:
#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,x,y,j=10000,a[1000010][3],b[10010],c[10010],ma,t;
int main()
{
freopen("friend.in","r",stdin);
freopen("friend.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
a[i][1]=x;
a[i][2]=y; //存关系
b[x]++;
b[y]++; //累加朋友数
if (b[x]>ma||b[x]==ma&&j>x)
{
ma=b[x];
j=x;
}
if (b[y]>ma||b[y]==ma&&j>y)
{
ma=b[y];
j=y;
} //更新最大值
}
for (int i=1;i<=m;i++)
{
if (a[i][1]==j)
c[++t]=a[i][2];
if (a[i][2]==j)
c[++t]=a[i][1];
} //找出所有朋友
sort(c,c+t+1); //排序
for (int i=1;i<=t;i++)
printf("%d ",c[i]);
fclose(stdin);
fclose(stdout);
return 0;
}
T2
题目:
在统计完朋友情况之后,小明又对大家的毕业学校产生兴趣,但是他觉得单纯的统计人数是一件非常无聊的事情,于是他设计了一个算法,同一所学校毕业的学生,第1个将获得1分,第2个获得2分,第3个获得4分…,第i个将获得2^(
i
i
i-1)分,总分就是这所小学的得分,小明想知道得分最高的学校有多少分。
输入:
第一行有两个整数
n
n
n和
m
m
m,
n
n
n表示总人数,
m
m
m表示已知的同校关系数量。
接下来
m
m
m行,每行有2个以空格隔开的整数
a
a
a和
b
b
b,表示
a
a
a和
b
b
b是来自同一所学校,
a
a
a和
b
b
b均为1到
n
n
n之间的整数。不会给出重复的信息。
输出:
只有一行,为所有学校中的最高得分。最后得分可能会很大,你只需要输出后100位即可,不足100位的请直接输出。
样例:
input
5 3
1 2
3 4
1 3
output
15
数据范围限制:
60%的数据,1 <= n <= 10
80%的数据,1 <= n <= 70
100%的数据,1 <= n <= 10000,1 <= m <= 100000
提示:
1、2、3、4来自同一所学校,该所学校所得的分数为1+2+4+8=15
解题思路:
并查集+高精度
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,x,y,ma,xf,yf,len1=1,a[100010],c[130],f[12000];
int getfather(int x)
{
if (f[x]==x) //当自己是自己的祖先,就返回
return x;
return f[x]=getfather(f[x]); //继续找祖先,路径压缩,所有人直接归属于自己的祖先
}
void cf() //高精乘
{
int t=0;
for (int i=1;i<=min(len1,100);i++)
{
c[i]=c[i]*2+t;
if (c[i]>=10)
{
t=c[i]/10;
c[i]=c[i]%10;
}
else t=0;
}
while (t>0)
{
c[++len1]=t%10;
t=t/10;
}
}
int main()
{
freopen("score.in","r",stdin);
freopen("score.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
a[i]=1,f[i]=i; //初值每个人都是自己的祖先
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
xf=getfather(x);
yf=getfather(y); //找祖先
if (xf!=yf) //祖先不一样合并
{
f[xf]=yf; //将y的祖先变成他们共同的祖先
a[yf]+=a[xf]; //累加人数
ma=max(ma,a[yf]); //找最多的
}
}
c[1]=1;
for (int j=2;j<=ma+1;j++) //题意是求1+2+4...2^(n-1),其实就是2^n-1
cf();
len1=min(100,len1);
int j=1;
c[1]--; //最终乘积-1
while (c[j]<0)
{
c[j]+=10;
c[++j]--;
}
if (c[len1]==0)
len1--;
for (int i=len1;i>0;i--) //倒序输出
printf("%d",c[i]);
fclose(stdin);
fclose(stdout);
return 0;
}
T3
题目:
小明迷恋上了一个新的跳棋游戏,游戏规则如下:棋盘是一排从0开始,顺序编号的格子,游戏开始时你位于0号格子,你每次只能往编号大的格子跳,而且你每次至少需要跳过L个格子,至多只能跳过R个格子。每个格子都有一个给定的伤害值,显然你希望得到的伤害值越少越好。
你能告诉小明他当他跳到最后一个格子时受到的累积伤害值最小为多少吗?
如果无论如何小明都无法跳到最后一个格子,这个时候你需要输出”-1”。
注:从i号格子跳过x个格子表示从i号格子跳到第i+x+1号格子。
输入:
输入文件jump.in第一行有三个整数n、L和R,n表示格子的编号从0到n。L和R表示最少需要跳过的格子数和最多能够跳过的格子数。
第二行有n个正整数,两个数字间用空格隔开,表示每个格子的伤害值。
输出:
输出文件jump.out仅有一个整数,表示受到的最小伤害值,保证结果小于maxlongint。
样例:
input
10 2 6
1 3 5 7 9 2 4 6 8 10
output
12
数据范围限制:
50%的数据,1 <= n <= 1000
65%的数据,1 <= n <= 10000
100%的数据,1 <= n <= 1000000,1 <= L <= R <= n
其中有15%的数据,1 <= n <= 1000000,1 <= L <= R <= 10
解题思路:
正解DP+单调队列
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long n,l,r,t,h=1,f[1000010],a[1000010],m[1000010];
int main()
{
freopen("jump.in","r",stdin);
freopen("jump.out","w",stdout);
memset(f,0x7f,sizeof(f));
scanf("%lld%lld%lld",&n,&l,&r);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]);
f[0]=0;
for (int i=1;i<=n;i++)
{
while (h<=t&&m[h]<i-r-1)
++h; //在范围内
if (i-l-1>=0&&f[i-l-1]!=9187201950435737471) //左边没超界
{
while (h<=t&&f[m[h]]>=f[i-l-1]) //在范围内
--t;
m[++t]=i-l-1; //存新的点
}
if (h<=t) //没有超范围
f[i]=f[m[h]]+a[i];
}
if (f[n]!=9187201950435737471) //能到达
cout<<f[n]<<endl;
else cout<<-1<<endl;
fclose(stdin);
fclose(stdout);
return 0;
}
T4
题目:
在跳棋游戏大获全胜后,小明就开始一个人在校园里溜达了。突然他在校园角落里发现了一面神奇的墙壁,墙壁上有一排钉子,每个钉子上都挂着一根两端系有小球的绳子,如下图所示
小明可以调整每一根绳子在钉子左右两端的长度,当来自不同绳子的相邻小球高度一样时(具体可见样例说明),便可获得积分1分。当小明的方案获得最高积分时,迷宫大门就会开启,小明就可以进去寻找宝藏啦!
输入:
第一行为一个正整数
n
n
n,表示墙上的绳子数。
接下来
n
n
n行,每行2个整数
a
a
a和
b
b
b,表示绳子左右两端的初始长度。
输出:
仅有一个正整数,表示小明可以获得的最高积分。
样例:
input
3
1 1
3 2
1 4
output
2
数据范围限制:
解题思路:
贪心
代码:
#include<iostream>
#include<cstdio>
using namespace std;
int x,y,n,l,r,ans,a[500020];
int main()
{
freopen("door.in","r",stdin);
freopen("door.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
a[i]=x+y; //整条的长度
}
l=0,r=a[1]; //最短为0,最长为a[1]
for (int i=2;i<=n;i++)
{
if (a[i]<l) //比最短的还小
{
l=0;
r=a[i]; //重置,最短为0,最长为a[i]
}
else
{
if (r<a[i]) //大于最长
x=a[i]-r; //最短将更新为a[i]-r(因为左边最长为r,剩下的都给右边)
else x=0;
r=a[i]-l; //最长更新为a[i]-l(因为左边最短为l,剩下的给右边)
l=x;
ans++; //累加得分
}
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}