膜你赛2018-1-31

蒟蒻感想

感觉今天的题真是难得过分
一道数据结构也不出
只出了一道堆,还是用stl过的…
不扯淡了,蒟蒻这就上题解

T1

洛谷题目传送门
我会尽力给大家找到非本校OJ的题目链接,当然很可能找不到,您们可以在评论区把链接发过来,我好补上
截个题面
这里写图片描述
这里写图片描述
这里写图片描述
杀马特是什么鬼
我们学校的题目描述不是这样的
此题大力贪心即可
对于比 c ≥ c 的钞票直接大力一张一张丢就好了
现在考虑面值比c小的钞票
是用贪心大法
怎么贪心呢
显然面值比较小的硬币可以“填缝”,创造比较大的价值,比如 c=61+5=6 c = 6 1 + 5 = 6 ,单独一张5虽然面值大也没有用
所以贪心的去取
先取大的面值
取不到c就结束了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<limits.h>
#define MAXN 25
using namespace std;
struct shit
{
  long long v,k;
}cow[MAXN];
bool mmp(shit a,shit b)
{
  return a.v<b.v;
}
long long n,c,fuck[MAXN];
long long ans;
int main()
{
  scanf("%lld%lld",&n,&c);
  for(long long i=1;i<=n;i++)
  scanf("%lld%lld",&cow[i].v,&cow[i].k);
  sort(cow+1,cow+n+1,mmp);
  long long left=1,right=1;
  for(long long i=n;i;i--)
  {
    if(cow[i].v<c)
    {
      right=i;
      break;
    }
    ans+=cow[i].k;
    if(i==1)
    {
      cout<<ans<<endl;
      return 0;
    }
  }
  while(left<=right)
  {
    long long temp=0;
    memset(fuck,0,sizeof(fuck));
    for(long long i=right;i>=left;i--)
    {
      long long sum=(c-temp)/cow[i].v;
      fuck[i]=min(sum,cow[i].k);
      temp+=fuck[i]*cow[i].v;
      if(temp>=c)break;
    }
    if(c-temp)
    {
      for(long long i=left;i<=right;i++)
      {
        if(cow[i].v>=c-temp&&cow[i].k-fuck[i])
        {
          fuck[i]++;
          temp+=cow[i].v;
          break;
        }
      }
    }
    if(temp<c)
    {
      printf("%lld\n",ans);
      return 0;
    }
    long long t=INT_MAX;
    for(long long i=left;i<=right;i++)if(fuck[i])t=min(t,cow[i].k/fuck[i]);
    ans+=t;
    for(long long i=left;i<=right;i++)if(fuck[i])cow[i].k-=t*fuck[i];
  }
}

变量名有点怪,但我不想改了

T2

此题是本次考试最难的一题
T3,T4跟他一比简直水爆
我最后A的这道题,想了好长时间,WA了好几次(多亏了ACM赛制)
洛谷题目传送门
这里写图片描述
这里写图片描述
这里写图片描述
难度一下跳到了省选-
我一开始想到最大连续子段和,想要贪心 O(n) O ( n ) 做,但是只有28分QAQ
在此基础上改动
二分答案+大力最大连续子段和
对于一个二分得到的答案ans,我们怎么验证呢?
以及为什么要提到最大连续子段和呢?
首先,我们在有ans的情况下,可以算出每个数和ans的差
假设我们去掉的一段值为value,长度为len
那么对应原数列中的就为 value+ans×len v a l u e + a n s × l e n
剩余数的平均值就为 totvalueans×lennlen t o t − v a l u e − a n s × l e n n − l e n
把它和ans作比较 totvalueans×lennlenans t o t − v a l u e − a n s × l e n n − l e n − a n s
=totvalueans×nnlen = t o t − v a l u e − a n s × n n − l e n
所以验证剩余数的平均分是否 ans ≤ a n s 就验证一下 totvalueans×n0 t o t − v a l u e − a n s × n ≤ 0 是否成立
显然value越大越妙
PS:这道破题卡精度,精度稍微要大一点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100010
using namespace std;
int a[MAXN],n,tot;
double temp[MAXN];
inline bool check(double ans)
{
  for(int i=1;i<=n;i++)temp[i]=(double)a[i]-ans;
  double now=0,biggest=-10000000000;
  for(int i=2;i<n;i++)
  {
    now+=temp[i];
    biggest=max(biggest,now);
    if(now<0)now=0.0000000;
  }
  return tot-biggest-ans*n<=0;
}
int main()
{
  scanf("%d",&n);
  double left=0,right=10000;
  for(int i=1;i<=n;i++)scanf("%d",&a[i]),tot+=a[i];
  while(right-left>=0.0001)
  {
    double mid=(left+right)/2;
    if(check(mid))right=mid;
    else left=mid+0.00001;
  }
  printf("%.3lf\n",(left+right)/2);
}

T3

这道题没有找到其他网站的链接
只能截题面了
这里写图片描述
此题巨傻无比
先按照分数排序
枚举中位数
是中位数的条件就是他的前面有 n2 n 2 个数,他的后面也有 n2 n 2 个数
我们维护一下在他前面取 n2 n 2 个数的最小花费,在他后面取 n2 n 2 个数的最小花费
O(n) O ( n ) 枚举
维护最小花费的操作要用堆实现
预计时间复杂度 O(nlog2n) O ( n log 2 ⁡ n ) ,一点都不虚

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define MAXN 100100
#define ll long long
using namespace std;
inline void read(ll &x)
{
  ll s=0,w=1;
  char c=getchar();
  while(c<'0'||c>'9')
  {
    if(c=='-')w=-1;
    c=getchar();
  }
  while(c>='0'&&c<='9')
  {
    s=(s<<3)+(s<<1)+c-'0';
    c=getchar();
  }
  x=s*w;
}
struct node
{
  ll s,q;
}cow[MAXN];
ll n,c,f,ans=-1,qianyiban[MAXN],houyiban[MAXN];
inline bool mmp(node a,node b)
{return a.s<b.s;}
priority_queue<ll>Q;
int main()
{
  read(n),read(c),read(f);
  for(int i=1;i<=c;i++)read(cow[i].s),read(cow[i].q);
  sort(cow+1,cow+c+1,mmp);
  while(!Q.empty())Q.pop();
  for(int i=1;i<=c;i++)
  {
    if(Q.size()<n/2)Q.push(cow[i].q),qianyiban[i]=qianyiban[i-1]+cow[i].q;
    else
    {
      if(cow[i].q<Q.top())
      {
        qianyiban[i]=qianyiban[i-1]-Q.top()+cow[i].q;
        Q.pop();
        Q.push(cow[i].q);
      }
      else qianyiban[i]=qianyiban[i-1];
    }
  }
  while(!Q.empty())Q.pop();
  for(int i=c;i>=1;i--)
  {
    if(Q.size()<n/2)Q.push(cow[i].q),houyiban[i]=houyiban[i+1]+cow[i].q;
    else
    {
      if(cow[i].q<Q.top())
      {
        houyiban[i]=houyiban[i+1]-Q.top()+cow[i].q;
        Q.pop();
        Q.push(cow[i].q);
      }
      else houyiban[i]=houyiban[i+1];
    }
  }
  for(int i=n/2+1;i+n/2<=c;i++)
  {
    if(qianyiban[i-1]+cow[i].q+houyiban[i+1]<=f)ans=max(ans,cow[i].s);
  }
  if(ans<0)
  {
    puts("-1");
    return 0;
  }
  printf("%lld\n",ans);
}

T4

这个也没找到出处
这里写图片描述
这个没什么好说,退化版带权并查集,我并不会带权并查集
就是将一头牛i拆成i和i+n(i+n是i的对立面)
如果i和i+n在一个并查集里就是矛盾了
A说B说假话的条件A,B属于不同的集合(一真一假)
A说B说真话的条件A,B属于同一集合(两真或两假)

#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 2010
using namespace std;
int fa[MAXN];
int find(int x)
{
  if(fa[x]==x)return x;
  fa[x]=find(fa[x]);
  return fa[x];
}
inline void un(int x,int y)
{
  x=find(x),y=find(y);
  if(x^y)fa[y]=x;
}
int n,m,ans;
bool flag;
int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1;i<=2*n;i++)fa[i]=i;
  for(int i=1;i<=m;i++)
  {
    int x,y;
    scanf("%d%d",&x,&y);
    char c=getchar();
    while(c!='L'&&c!='T')c=getchar();
    if(flag)continue;
    if(c=='L')un(x,y+n),un(y,x+n);
    else un(x,y),un(x+n,y+n);
    if(find(x)==find(x+n)||find(y)==find(y+n))
    {
      flag=1;
      ans=i-1;
    }
  }
  if(!flag)ans=m;
  printf("%d\n",ans);
}

差不多就这样了
如发现有任何错误请联系作者
仅代表DSFZ最低水平

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值