Problem C
每个数据记录属于哪个人和数值大小,按照数值大小排序。
确定我们目标数值在其中的区间
第一个目标值出现的位置记做pos_left,
最后一个目标值出现的位置记做pos_right,
我们所需这个数据出现的位置need_pos = (F*M+1)/2
如果pos_left>need_pos,对pos_left左边的数据统计各个人拥有的数量,先把数据最多的人用掉可以减少人数。
pos_right<need_pos,pos_right,对pos_right右边进行相同操作
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define ll long long
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
using namespace std;
#define mod 1007
const int N = 1e6+5;
struct node
{
int v;
int people;
bool operator < (const node& a)const{
return v < a.v;
}
}p[N];
int v[N];
int cnt[1010];///记录所需区间内各个人的数据共出现几次
int main()
{
//int T;scanf("%d",&T);
int F,M,goal;
while (~scanf("%d %d %d",&F,&M,&goal)){
memset(cnt,0,sizeof cnt);
for1(i,1,F*M){
scanf("%d",&p[i].v);
p[i].people = (i-1)/M+1;
}
sort(p+1,p+1+F*M);
for1(i,1,F*M) v[i] = p[i].v;
int need_pos = (F*M+1)/2;
int pos_left = lower_bound(v+1,v+1+F*M,goal) - v;
int pos_right = upper_bound(v+1,v+1+F*M,goal) - v - 1;
if (need_pos>=pos_left && need_pos <= pos_right) {printf("0 0\n");continue;}
//printf("pos_l=%d pos_r=%d\n",pos_left,pos_right);
if (v[1]>goal) pos_left = pos_right = 0;
if (v[F*M]<goal) pos_right ++;
//printf("pos_l=%d pos_r=%d\n",pos_left,pos_right);
int update_cnt;
int ans=0,ans_cnt;///记录修改的人数,修改的总数
if (pos_left>need_pos){/// 需要从左边去掉几个数
update_cnt = pos_left-need_pos;
ans_cnt = update_cnt;
for1(i,1,pos_left-1) cnt[p[i].people]++;
//for1(i,1,F) printf("第%d个人的数据个数=%d\n",i,cnt[i]);
sort(cnt+1,cnt+F+1,[](int a,int b){return a>b;});
for1(i,1,F){
update_cnt -= cnt[i];
ans++;
if (update_cnt<=0) break;
}
}
else if (pos_right<need_pos){///从右边去掉几个数
update_cnt = need_pos-pos_right;
ans_cnt = update_cnt;
for1(i,pos_right+1,F*M) cnt[p[i].people]++;
sort(cnt+1,cnt+F+1,[](int a,int b){return a>b;});
for1(i,1,F){
update_cnt -= cnt[i];
ans++;
if (update_cnt<=0) break;
}
}
printf("%d %d\n",ans,ans_cnt);
}
return 0;
}
Problem D
现在只考虑1/8块,紧贴x轴正方向的上半块三角形,最后求出答案*8就行了
我们需要一条直线在范围内有C个整数点,由于整数点在直线上均匀分布,我们只需判断
我们将区间分为C+1部分,那么第一个点整数点x一定要大于n/(C+1),否则就至少有C+1个整数点了
将区间分为C个部分,那么第一个整数点又要小于等于n/C,否则就不够C个整数点了,
所以线段第一个点被限制在这个(n/(c+1),n/c]中
我们在这个区域找所有线段的第一个整数点,怎么样才能确保这个点是线段第一个整数点呢?
互质就可以了,然后问题就变成了对于x(n/(c+1),n/c]求每个x小于自身的互质的数的个数
用欧拉筛即可O(N)求出所有点小于自身的互质的数
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define ll long long
#define mod 1000000007
using namespace std;
const int N = 5e6+5;
bool vis[N];
int prime[N],tot;
int phi[N];
void euler()
{
tot = 0;
phi[1] = 1;
for1(i,2,5000000){
if (!vis[i]){
phi[i] = i-1;
prime[tot++] = i;
}
for (int j=0;j<tot && i*prime[j]<=5000000;j++){
vis[i*prime[j]] = true;
if (i%prime[j]==0){
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
else phi[i*prime[j]] = phi[i]*phi[prime[j]];
}
}
}
int main()
{
euler();
int n,c;
scanf("%d %d",&n,&c);
int minv = n/(c+1);
int maxv = n/c;
ll ans = 0;
for (int i=minv+1;i<=maxv;i++)
ans += phi[i];
ans = ans*8;
printf("%lld\n",ans);
return 0;
}
Problem F
先选取一半人作为主场,此时共种方案,因为比赛是无序的,所以这取出的一半人就放在主场,接下去就是另一半人
去挑选和主场的谁比,此时是种,两个相乘就是
接下去反转,原先主场那批人现在去客场,等待对手来匹配,剩下一半人由于不能再找自己原来的对手,所以就变成了一个错排问题:(n/2)个人进行排序,每个人不能到和自己编号一样的位置,问共有多少种方案?
有个DP的公式:dp[i] = (i-1)*dp[i-1]+(i-1)*dp[i-2]
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define ll long long
#define mod 1000000007
using namespace std;
const int N = 5e5+5;
ll dp[N];
void prework()
{
dp[1] = 0;
dp[2] = 1;
for1(i,3,250000) dp[i] = (i-1)*(dp[i-1]+dp[i-2])%mod;
}
int main()
{
prework();
ll n;
while (~scanf("%lld",&n)){
ll ans = 1;
for (ll i=n/2+1;i<=n;i++) ans = (ans*i)%mod;
ans = ans*dp[n/2]%mod;
printf("%lld\n",ans);
}
return 0;
}