2019牛客多校第六场

A.Garbage Classification

按照题意模拟就好
也许可能要当心那个除法?建议转换成乘法做

#include <bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int T,dry,wet,harm,t,n,i,x;
char ch[3000],s[100];
int main()
{
    scanf("%d",&T);
    fo(t,1,T)
    {
        dry = wet = harm = 0;
        scanf("%s",ch+1); n = strlen(ch+1);
        scanf("%s",s+1);
        fo(i,1,n)
        {
            x = ch[i] - 'a' + 1;
            if (s[x] == 'd') dry++;
            if (s[x] == 'w') wet++;
            if (s[x] == 'h') harm++;
        }
        cout<<"Case #"<<t<<": ";
        if (4 * harm >= n) cout<<"Harmful"<<endl;
        else if (10 * harm <= n) cout<<"Recyclable"<<endl;
        else if (dry >= 2 * wet) cout<<"Dry"<<endl;
        else cout<<"Wet"<<endl;
    }
    return 0;
}

B.Shorten IPv6 Address

同样也是按照题意模拟就好
首先把不省略 0 0 0的答案做出来,然后考虑去掉哪一段 0 0 0
这个问题需要大量的讨论…
比如 ′ 0 ′ &#x27;0&#x27; 0 ′ : ′ &#x27;:&#x27; : a s c i i ascii ascii码,比如两端的 ′ 0 ′ &#x27;0&#x27; 0和中间的 ′ 0 ′ &#x27;0&#x27; 0去掉后对于长度的贡献不一样
当然最方便的做法就是暴力去掉每一段 ′ 0 ′ &#x27;0&#x27; 0然后排个序就好了

D.Move

首先看到这个题的过题比率就能猜到肯定不是简单的二分。。。
题解是真的厉害,我都构造不出什么hack

155
39 39 39 39 39 60 60 60 60 60 100 100 100 100 100
199 为一个合法的答案,但 200 不是,201 也不是。

最后我们的过题策略是。。:
首先我猜测这个题大范围上一定满足二分的性质
那么哪里有可能不满足?我猜是边界条件
所以我先从下界开始check300个答案(复杂度上限)如果找不到答案就再二分
然后就a了。。。
然后开始瞎搞。。发现只要额外check下界就好了。。
现在复盘一下,感觉就是不满足二分的答案少之又少,而这些答案都有一个特点:他们都是下界
实际上这个题,我的下界是对的( ⌈ s u m k ⌉ \lceil \frac{sum}{k} \rceil ksum)但是我的上界算大了
对于一个答案 a n s ans ans,如果它非法,那么显然每个箱子空余空间 &lt; m a x V &lt;maxV <maxV
所以有 k ∗ ( a n s − m a x V + 1 ) ≤ s u m k*(ans-maxV+1) \le sum k(ansmaxV+1)sum(即并没有装下所有物品)
化简一下就可以得到 a n s ≤ s u m / k + m a x V − 1 ans \le sum/k+maxV-1 anssum/k+maxV1
所以上界就是 ⌈ s u m k ⌉ + m a x V − 1 \lceil \frac{sum}{k} \rceil+maxV-1 ksum+maxV1
这个区间长度只有 m a x V maxV maxV,所以暴力check每一个答案就好
时间复杂度是 O ( m a x V n l o g n ) O(maxVnlogn) O(maxVnlogn),跑满的话是 2 e 8 2e8 2e8(实际上怎么可能跑满)
当然你也可以强行二分
f ( x ) , f ( x + m a x V ) , f ( x + 2 ∗ m a x V ) , . . . , f ( x + k ∗ m a x V ) f(x),f(x+maxV),f(x+2*maxV),...,f(x+k*maxV) f(x),f(x+maxV),f(x+2maxV),...,f(x+kmaxV)是单调的
(每个箱子都大 m a x V maxV maxV那肯定能多装嘛(虽然感觉还是有点怀疑))
所以可以先二分再暴力check,时间复杂度会多一个log,不过考虑到check成功的概率极高,所以还是能过的(吧)

#include <bits/stdc++.h>
using namespace std;
multiset <int> ms;
int n,k,a[1005];
bool check(int x)
{
    ms.clear();
    for (int i=0;i<n;i++)
        ms.insert(a[i]);
    for (int i=0;i<k;i++)
    {
        int tmp=x;
        while (true)
        {
            if (!ms.size()) break;
            if (tmp<*(ms.begin())) break;
            else
            {
                multiset<int> ::iterator it=ms.upper_bound(tmp);
                it--;
                int cur=*it;
                ms.erase(ms.lower_bound(cur));
                tmp-=cur;
            }
        }
    }
    if (ms.size()) return false;
    return true;
}
 
int main()
{
    int t;
    scanf("%d",&t);
    for (int cas=1;cas<=t;cas++)
    {
        scanf("%d%d",&n,&k);
        for (int i=0;i<n;i++)
            scanf("%d",&a[i]);
        int flag = 0;
        int l = 0; for (int i=0;i<n;i++) l += a[i];
        if (l % k == 0) l = l / k; else l = l / k + 1;
        for (int i=l;i<=l;i++)
            if (check(i))
            {
                int ans = i;
                printf("Case #%d: %d\n",cas,ans);
                flag = 1;
                break;
            }
        if (flag) continue;
        int ans=1e6;
        while (true)
        {
            int l=1,r=ans;
            while (l<=r)
            {
                int mid=(l+r)>>1;
                if (check(mid)) r=mid-1;
                else l=mid+1;
            }  
            if (l==ans) break;
            else ans=l;
        }
        printf("Case #%d: %d\n",cas,ans);
    }
    return 0;
}

E.Androgynos

听说还有论文呢orz
场上没做出来真的好可惜
首先根据边是偶数这个条件可以得到 n = 4 k 或 者 4 k + 1 n=4k或者4k+1 n=4k4k+1
然后就可以分成 4 4 4堆,每堆 k k k
其中两堆是团(两两连边)另外两堆是独立集(互不连边)
之后对这 4 4 4堆按照 n = 4 n=4 n=4的情况进行堆与堆之间的连边
如果多一个点,就把这个点和两个团连
注意, n = 4 n=4 n=4的时候是一条链,那么我们要保证中间两堆应该都是团(或者都是独立集)
至于为什么。。手动模拟就知道错哪了
换个通俗易懂的讲法:
[1,k]连[k+1,2k] [k+1,2k]连[2k+1,3k] [2k+1,3k]连[3k+1,4k]
然后[k+1,2k]和[2k+1,3k]自己内部连
如果多一个怎么办?那就向[k+1,3k]连

#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int T,cas,x,i,j,n;
int f[2005][2005],g[2005];
int main()
{
    cin>>T;
    fo(cas,1,T)
    {
        cin>>x;
        cout<<"Case #"<<cas<<": ";
        if (x % 4 != 0 && x % 4 != 1) {cout<<"No"<<endl; continue;} else cout<<"Yes"<<endl;
        n = x / 4;
        fo(i,1,x) fo(j,1,x) f[i][j] = 0;
        fo(i,1,n) fo(j,1,n) if (i != j) f[i][j] = 1;
        fo(i,n+1,2*n) fo(j,n+1,2*n) if (i != j) f[i][j] = 1;
        fo(i,1,n) fo(j,n+1,2*n) if (i != j) f[i][j] = f[j][i] = 1;
        fo(i,1,n) fo(j,2*n+1,3*n) if (i != j) f[i][j] = f[j][i] = 1;
        fo(i,n+1,2*n) fo(j,3*n+1,4*n) if (i != j) f[i][j] = f[j][i] = 1;
        if (x % 4 == 1) fo(i,1,2*n) f[x][i] = f[i][x] = 1;
        fo(i,1,n) g[i] = 2*n+i;
        fo(i,n+1,2*n) g[i] = 2*n+i;
        fo(i,2*n+1,3*n) g[i] = i-n;
        fo(i,3*n+1,4*n) g[i] = i-3*n;
        if (x % 4 == 1) g[x] = x;
        fo(i,1,x)
        {
            fo(j,1,x) cout<<f[i][j]; cout<<endl;
        }
        fo(i,1,x-1) cout<<g[i]<<" "; cout<<g[x]<<endl;
    }
    return 0;
}

G.Is Today Friday?

是个乱搞题,还是非常有意思的(主要是因为自己做出来了23333)
首先介绍我的做法
做这个题的时候第一反应就是这个解不会很多(怎么可能这么凑巧两组对应方案下一堆日期都是星期五?)
所以我首先全排列,然后随机取出100个日期check是不是星期五
在绝大部分情况下,这100次check足够了
如果有数据能够完美的躲过100次check,那说明这组数据大部分应该是满足的
然后我对这组数据全体check
这个时候最关键的部分来了:我认为如果这组数据有75%以上都是星期五,但不全是星期五,那就是 i m p o s s i b l e impossible impossible
因为我觉得怎么可能存在两组对应使得大部分日期都是星期五嘛
调了一下参突然就a了哈哈哈哈哈
其实这题不管怎么调参都是可以的,一开始wa的原因是因为没有暴力判断小数据
毕竟一组数据如果有一半不是星期五,那它躲过100次测试的概率低达 1 2 100 \frac{1}{2^{100}} 21001
当时做题的时候简直就是 M i l l e r Miller Miller R o b i n Robin Robin附体哈哈哈哈借鉴了那个素性测试的思想
std的做法大同小异
实际上直接按照全排列一个一个check过来就行了,基本上check几个就会break
哦还有,数据中有一组应该是一堆重复日期,所以如果不去重的话就会T
然而我加了0.75就神奇的逃过一劫23333
改成0.74就T了哈哈哈哈
看来是猜中了出题人的数据

#include <bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int DateToInt(int m,int d,int y)
{
    return 1461*(y+4800+(m-14)/12)/4+367*(m-2-(m-14)/12*12)/12-3*((y+4900+(m-14)/12)/100)/4+d-32075;
}
bool isfri(int m,int d,int y)
{
    return DateToInt(m,d,y)%7==4;
}
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool isdate(int m,int d,int y)
{
    if (y < 1600 || y > 9999) return false;
    if (m < 1 || m > 12) return false;
    int feb;
    if (y % 400 == 0 || (y % 4 == 0 && y % 100 != 0)) day[2] = 29; else day[2] = 28;
    if (d < 1 || d > day[m]) return false;
    return true;
}
int T,cas,n,i,times,k,flag,t,yy,mm,dd,num;
char ch[100];
int f[100005][10];
int b[100005];
int a[15];
int main()
{
    srand(233336666);
    scanf("%d",&T);
    fo(cas,1,T)
    {
        scanf("%d",&n);
        fo(i,1,n)
        {
            scanf("%s",ch);
            f[i][1] = ch[0] - 'A';
            f[i][2] = ch[1] - 'A';
            f[i][3] = ch[2] - 'A';
            f[i][4] = ch[3] - 'A';
            f[i][5] = ch[5] - 'A';
            f[i][6] = ch[6] - 'A';
            f[i][7] = ch[8] - 'A';
            f[i][8] = ch[9] - 'A'; 
        }
        fo(i,0,9) a[i]=i;
        times = 0;
        do
        {
            //fo(i,0,9) printf("%d",a[i]); printf("\n");
            times++;
            //if (times >= 9) break;
            k = min(100,n);
            flag = 0;
            while (k--)
            {
                t = rand()%n+1;
                while (b[t] == times) t = rand()%n+1;
                b[t] = times;
                yy = a[f[t][1]]*1000+a[f[t][2]]*100+a[f[t][3]]*10+a[f[t][4]];
                mm = a[f[t][5]]*10+a[f[t][6]]; dd = a[f[t][7]]*10+a[f[t][8]];
                if (isdate(mm,dd,yy) == false) {flag = 1; break;}
                if (isfri(mm,dd,yy) == false) {flag = 1; break;}
            }
            if (flag == 0)
            {
                num = 0;
                fo(t,1,n)
                {
                    yy = a[f[t][1]]*1000+a[f[t][2]]*100+a[f[t][3]]*10+a[f[t][4]];
                    mm = a[f[t][5]]*10+a[f[t][6]]; dd = a[f[t][7]]*10+a[f[t][8]];
                    if (isdate(mm,dd,yy) == false) {flag = 1; break;}
                    if (isfri(mm,dd,yy) == false) {flag = 1; break;}
                    num++;
                }
                if (flag && num > 0.75 * n && n > 100) break;
            }
            if (flag == 0)
            {
                printf("Case #%d: ",cas);
                fo(i,0,9) printf("%d",a[i]); printf("\n");
                break;
            }
        }while (next_permutation(a,a+10));
        if (flag == 1)
        {
            printf("Case #%d: ",cas);
            printf("Impossible\n");
        }
    }
     
    return 0;
}

J.Upgrading Technology

首先我对升级的价格取负,这样题意中所有的数据都会变成“奖励”
我们让这个“奖励”最大即可
枚举最小等级,然后找出每个技能升到几级最赚,这个用优先队列维护前缀和就可以了
然后需要把某一个技能压到 j j j级,那当然是赚得最少的那个变成 j j j
注意这里不是找最小的 p r e [ i ] [ k ] pre[i][k] pre[i][k],而是找最小的 p r e [ i ] [ k ] − p r e [ i ] [ j ] pre[i][k]-pre[i][j] pre[i][k]pre[i][j],即 j j j级以上的部分赚得最少的
还有就是优先队列的前缀和要多扔一个0级,因为可以不升

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1007;
LL c[maxn][maxn];
LL d[maxn];
struct Node{
    LL cost;
    int pos;
    bool operator < (const Node &rhs) const
    {
        return cost<rhs.cost;
    }
};
priority_queue<Node> q[maxn];
int main (){
    int T;
    scanf("%d", &T);
    for (int t = 1; t <= T; t++)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        for (int i=1;i<=n;i++)
            while (!q[i].empty())
                q[i].pop();
        for (int i = 1; i<= n; i++){
            for (int j = 1; j <= m; j++){
                scanf("%lld", &c[i][j]);
                c[i][j] = -c[i][j];
                c[i][j] = c[i][j-1] +c[i][j];
                q[i].push(Node{c[i][j],j});
            }
            q[i].push(Node{0,0});
        }
        for (int i=1;i<=m;i++)
            scanf("%lld",&d[i]);
        for (int i=1;i<=m;i++)
            d[i]+=d[i-1];  
        LL ans = 0;
        for (int j = 0; j <= m; j++){
            int pos = 0;
            LL tmp = 0;
            LL mi = 1LL << 60;
            for (int i = 1; i <= n; i++) {
                while (!q[i].empty() && q[i].top().pos<j)
                    q[i].pop();
                if (q[i].top().cost-c[i][j]< mi)
                {
                    mi = q[i].top().cost-c[i][j], pos = i;
                }
                tmp+=q[i].top().cost;
            }
            tmp-=q[pos].top().cost;
            tmp+=c[pos][j];
            tmp+=d[j];
            ans=max(ans,tmp);
        }
        printf("Case #%d: ", t);
        printf("%lld\n",ans);
    }
     
    return 0;  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值