SDUT 2018 Winter Individual Contest - 1

SDUT 2018 Winter Individual Contest - 1
https://cn.vjudge.net/contest/207904#problem/A
A 尺取法经典入门
题意:找一段最长的区间,该区间内的元素种类个数小于等于k

#include<bits/stdc++.h>
using namespace std;
const int maxn=  1e6+7;
int a[maxn];
int book[maxn];
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    memset(book,false,sizeof(book));
    book[a[1]] = 1;
    int sl = 1;
    int cnt  =1;
    int rel =  1,rell = 1,relr = 1;
    for(int i=2;i<=n;i++)
    {
        book[a[i]]++;
        if(book[a[i]]==1)cnt++;
        while(cnt>k)
        {
            book[a[sl]]--;
            if(book[a[sl]]==0)cnt--;
            sl++;
        }
        if(i-sl+1>rel)
        {
            rel = (i-sl+1);
            rell = sl;
            relr = i;
        }
    }
    printf("%d %d\n",rell,relr);
}

C 类似于哈夫曼堆
题意:有n(1<=n<=10^5)堆石子,每堆有ai(1<=ai<=10^9)个石子,每次将一堆石子a合并到另一堆b花费为a,求把所有石子合并一起的最小花费。

不过没有那么简单,现在有m(1<=m<=10^5)个询问ki(1<=ki<=10^5),问每堆石子至多被合并ki次,求把所有石子合并在一起的最小花费。

题解:

首先想ki = 1的情形,不难想到,一堆石子被合并一次,一堆石子被合并一次…一堆石子被合并一次,这显然是让最少的石子去合并别的石子n-1次。

考虑ki = 2,一堆石子被合并二次,二堆石子被合并二次,四堆石子被合并二次…即每次*2。

很好理解,每次可以合并的堆的个数增加了,被合并的堆数也在增加。所以排序后,从大到小贪心即可。同时记录答案。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+7;
long long n,a[maxn],sum[maxn];
int i;
int main()
{
    while (cin>>n)
    {
        for(i=1; i<=n; i++)
            scanf("%I64d",&a[i]);
        sort(a+1,a+1+n);
        int m;
        cin>>m;
        sum[0] = 0;
        for(i=1; i<=n; i++)
        {
            sum[i] = sum[i-1]+a[i];
        }
        long long k;
        long long data = 0;
        for(i=1; i<n; i++)
        {
            data+=(n-i)*a[i];
        }
        n--;
        while(m--)
        {
            long long ans = 0;
            cin>>i;
            if(i==1)
                cout<<data<<endl;
            else
            {
                long long tn = n;
                long long  p = 1;
                k = i;
                while(tn-k>=0)
                {
                    ans+=p*(sum[tn]-sum[tn-k]);
                    tn-=k;
                    k*=i;//该层结点的个数K^(2,4,8,16)
                    p++;//形成的k叉树的深度
                }
                if(tn>=0)//不满时加上剩下的
                    ans+=p*sum[tn];
                cout<<ans<<endl;
            }

        }

    }
    return 0;
}

D 二分答案经典
(注意该题卡常)
在栗树的脚下有n只松鼠。第i棵树的第一个栗子在T i秒后立即落下,而后一个P i秒后再落下一个。松鼠的“大妈妈”希望他们尽可能快的带上自己的巢穴,以免大风暴来临!所以他们正在讨论在最短的时间里等待哪些树下采取足够的栗子。移动到位的时间是零,松鼠在那之后没有任何动作。

请求
计算最短的时间(多少秒)松鼠可以采取足够的栗子。
输入
第一行包含一个整数t,即每个测试用例的数量:
第一行分别包含整数m,n,k。
第二行分别包含整数T i(i = 1..m)。
第三行分别包含整数P i(i = 1..m)。
(同一行上的每个整数由至少一个空格字符分隔,测试用例之间没有添加的行)
产量
对于每个测试用例,在一行中输出一个以最短时间计算的整数。

#include<bits/stdc++.h>
#include <iostream>
using namespace std;
const int maxn  = 1e6+10;
long long a[maxn],b[maxn];
long long sum[maxn];
int n,m;
long long k;
int cmp(long long a,long long b)
{
    return a>b;
}
int  pan(long long  t)
{
    vector<long long>o;
    for(int i=1;i<=m;i++)
    {
        if(t<a[i]) continue;
        o.push_back( 1+(t-a[i])/b[i]);
    }
    long long  Sum = 0;
    sort(o.begin(),o.end(),cmp);
    for(int i=0;i<=min(n,(int )o.size())-1;i++)
    {
        Sum+=o[i];
    }
    return Sum>=k;
}
int main(){

   int T;
  scanf("%d",&T);
   while(T--)
   {
      scanf("%d%d%lld",&m,&n,&k);
       for(int i=1;i<=m;i++)
       {
           scanf("%lld",&a[i]);
       }
       for(int i=1;i<=m;i++)
       {
           scanf("%lld",&b[i]);
       }
       long long l = 1,r = 1e9,ans = r;
       while(l<=r)
       {
          long long mid = (l+r)/2;
           //cout<<l<<" "<<r<<endl;
           int f = pan(mid);
           if(f){
                ans = mid;
               r = mid-1;
           }
           else l=mid+1;
       }
       cout<<ans<<endl;
   }
    return 0;
}

G
题意:小象喜欢玩彩卡。

他有n张牌,每张都有两种颜色(正面的颜色和背面的颜色)。最初,所有牌都在正面朝上放在桌子上。小象可以把任何一张牌转到另一边。小象认为,如果至少有一半的牌具有相同的颜色(每张牌的上边的颜色被考虑),桌上的一套牌是有趣的。

帮助小象找到所需的最少的动作,使这一套n卡搞笑。

输入
第一行包含一个整数Ñ (1≤  ñ  ≤10 5) -的卡的数量。以下n行包含所有卡的描述,每行一张卡。这些卡片是由一对不超过10 9 - 双方颜色的正整数描述的。一行中的第一个数字是卡片正面的颜色,第二个是背面的数字。卡片正面的颜色可能与卡片背面的颜色一致。

行中的数字由单个空格分隔。

产量
在一行上打印一个整数 - 寻求的最小移动次数。如果不可能让这个设置变得有趣,请打印-1。

离散化一下然后暴力求最大值即可

#include <bits/stdc++.h>
using namespace std;
const int maxn  = 1100000;
  int Twos[maxn] ={0} ;
  int Front[maxn] ={0};
int a[maxn],b[maxn],c[maxn];
int main() {
  int n,req;
  cin>>n;
  req = (n+1)/2;
  int top =0;
  for(int i=1;i<=n;i++)
  {
      scanf("%d%d",&a[i],&b[i]);
      c[top++] = a[i];
      c[top++] = b[i];
  }
    sort(c,c+top);
  top = unique(c,c+top)-c;
  for(int i=1;i<=n;i++)
  {
      int x =lower_bound(c,c+top,a[i])-c;
      int y = lower_bound(c,c+top,b[i])-c;
      Twos[x]++;
      Twos[y]++;
      Front[x]++;
      if(x==y)Twos[x]--;
  }
  int ans = 1e6+7;
  for(int i=0;i<top;i++)
  {
      if(Twos[i]>=req) {
        ans = min(ans,max(req-Front[i],0));
      }
  }
  if(ans==(1e6+7))printf("-1\n");
  else cout<<ans<<endl;
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值