TCO2016乱做

来源:互联网 发布:泯然众人 知乎 编辑:程序博客网 时间:2024/05/22 16:16

目录

模拟&搜索

1A 250pts
1B 250pts
1C 250pts
2A 400pts

贪心

1B 500pts
1C 500pts

三分

1B 1000pts

动态规划

1A 500pts

数学相关

Regional Wildcard 250pts

题目

Round 1A

250pts

题意简述

给你一个形如HH:MM的时刻,问把时针分针倒换之后的时刻是多少。

数据范围

保证时针分针指向的是表盘的数字。

思路

直接模拟即可

代码

#include<cstdio>#include<string>#include<iostream>using namespace std;class EllysTimeMachine{    public:        int h,m,hh,mm;        string getTime(string t)        {            h=(t[0]-'0')*10+(t[1]-'0');            m=(t[3]-'0')*10+(t[4]-'0');            hh=(m/5+11)%12+1;            mm=h*5%60;            string ret;            ret+=(hh/10)+'0';            ret+=(hh%10)+'0';            ret+=':';            ret+=(mm/10)+'0';            ret+=(mm%10)+'0';            return ret;        }};

500pts

题意简述

n只袜子,并给出每只袜子的大小sizei。现在要让袜子配对,要求至少配出p对,使得最大的|sizeusizev|最小,求这个最小值。

数据范围

1n10001sizei109

思路

DP。
先排序。
f[i][j]表示前i个匹配j对所需要的最小的最大差距。
f[i][j]=min(max(f[i2][j1],abs(s[i]s[i1])),f[k][j])k[1,i1]
决策是是否和前一个配对。
这样复杂度是O(n3)难以通过。
我们可以对后面的f[k][j]做一个前缀min,这样复杂度O(n2)
一开始想成了二分答案二分图匹配= =

代码

#include<cstdio>#include<cstring>#include<cstdlib>#include<vector>#include<algorithm>using namespace std;class EllysSocks{    public:        int f[1010][1010];        int mn[1010][1010];        int n;        int getDifference(vector<int> s,int p)        {            memset(f,0x3f,sizeof(f));            memset(mn,0x3f,sizeof(mn));            n=s.size();            sort(s.begin(),s.end());            for (int i=1;i<n;i++)            {                f[i][0]=mn[i][0]=0;                for (int j=1;j<=min(p,(i+1)/2);j++)                {                    f[i][j]=min(f[i][j],mn[i-1][j]);                    if (i-2>=1)                        f[i][j]=min(f[i][j],max(abs(s[i]-s[i-1]),mn[i-2][j-1]));                    else                        f[i][j]=min(f[i][j],abs(s[i]-s[i-1]));                    mn[i][j]=min(mn[i-1][j],f[i][j]);                }            }            return f[n-1][p];        }};

Round 1B

250pts

题意简述

给出一个数n,每次可以将x变成其各位数字的平方和,如果x为质数就退出,问会出现几个不同的数。

数据范围

1n109

思路

略微观察之后会发现经过几次变换之后,数字会在[1,729]之间变化,开一个此范围的bool数组记录。
爆搜即可。

代码

#include<cstdio>#include<cmath>#include<cstring>using namespace std;class ExploringNumbers{    public:        bool vis[1010];        bool is_prime(int n)        {            if (n==1)                return false;            for (int i=2;i<=int(sqrt(n));i++)                if (n%i==0)                    return false;            return true;        }        int dfs(int x)        {            if (is_prime(x))                return 1;            if (x<=729)            {                if (vis[x])                    return -1;                vis[x]=1;            }            int tmp=0;            while (x>0)                tmp+=(x%10)*(x%10),x/=10;            tmp=dfs(tmp);            if (tmp==-1)                return -1;            else                return tmp+1;        }        int numberOfSteps(int n)        {            memset(vis,0,sizeof(vis));            return dfs(n);        }}

500pts

题意简述

给出n个数ai,和一些你所拥有的[1,9]数字的个数di,你可以用你拥有的数字替换掉数中某些数位的数,使得替换后的数之和最大,求这个和。

数据范围

1n50
1ai106
1di103

思路

贪心。
按位贪心,从最大的数字开始,每次选取尽量多的。

代码

#include<cstdio>#include<vector>using namespace std;class ReplacingDigit{    public:        int getMaximumStockWorth(vector<int> s,vector<int> d)        {            int fac10[10],f[100][20]={0},num[20]={0},cnt[20]={0};            int n,sum=0,last;            fac10[0]=1;            for (int i=1;i<=6;i++)                fac10[i]=fac10[i-1]*10;            n=s.size();            for (int i=0;i<n;i++)            {                int tmp=s[i],di=0;                while (tmp)                    f[di][tmp%10]++,tmp/=10,cnt[di]++,di++;            }            for (int i=0;i<9;i++)                num[i+1]=d[i];            for (int i=9;i>=0;i--)            {                last=cnt[i];                for (int j=9;j>=0;j--)                {                    if (last-f[i][j]<=0)                    {                        sum+=last*j*fac10[i];                        break;                    }                    last-=f[i][j];                    sum+=f[i][j]*j*fac10[i];                    if (last-num[j]<=0)                    {                        sum+=last*j*fac10[i];                        num[j]-=last;                        break;                    }                    last-=num[j];                    sum+=num[j]*j*fac10[i];                    num[j]=0;                }            }            return sum;        }};

1000pts

题意简述

给出n个物品,每个物品有一个限制pi,要求这个物品的能量值必须pi
再给出h个普通产生器,花费x可以使得hi产生器对[li,ri]范围内的每个物品增加x的能量。
还有一个特殊产生器,花费xt可以使得所有物品增加x的能量。
求满足所有限制x的最小值。

数据范围

给出种子,数据根据给出的生成方式生成。
1n,h,t105
1lirin
1pi107

思路

三分+贪心。
如果以特殊产生器的x为自变量,可以观察到普通产生器的费用是一个斜率单调不降的减函数,而特殊产生器的费用是一个一次函数。那么最终的费用一定是一个单峰函数。
我们可以三分这个x,对于每一个x我们可以求出每一个点最右能延伸的哪里(线段覆盖?)。贪心求出它的费用。

代码

#include<cstdlib>#include<vector>#include<cstring>#include<cstdio>using namespace std;class SettingShield{    public:        struct interval{            int l,r;        }L[100010];        int n,h,t;        int seq[100010],p[100010],rightmax[100010],tag[100010];        long long calc(int x)        {            for (int i=0;i<n;i++)                p[i]=max(0,seq[i]-x);            long long sum=1LL*x*t;            int used=0;            for (int i=0;i<n;i++)            {                used-=tag[i];                tag[i]=0;                p[i]=max(0,p[i]-used);                sum+=p[i];                used+=p[i];                tag[rightmax[i]+1]+=p[i];            }            return sum;        }        long long getOptimalCost(int _n,int _h,int _t,vector<int> val0,vector<int> a,vector<int> b,vector<int> m)        {            n=_n;            h=_h;            t=_t;            int dist;            seq[0]=val0[0];            for (int i=1;i<n;i++)                seq[i]=(1LL*a[0]*seq[i-1]+b[0])%m[0];            L[0].l=val0[1];            L[0].r=val0[2];            for (int i=1;i<h;i++)            {                L[i].l=min(1LL*n-1,(1LL*a[1]*L[i-1].l+b[1])%m[1]);                dist=L[i-1].r-L[i-1].l;                L[i].r=min(1LL*n-1,L[i].l+(1LL*a[2]*dist+b[2])%m[2]);            }            memset(rightmax,0xff,sizeof(rightmax));            for (int i=0;i<h;i++)                rightmax[L[i].l]=max(rightmax[L[i].l],L[i].r);            for (int i=1;i<n;i++)                if (rightmax[i-1]>=i)                    rightmax[i]=max(rightmax[i],rightmax[i-1]);            int l=0,r=1e7+10;            long long ans=1LL<<60;            for (int i=0;i<n;i++)                if (rightmax[i]==-1)                    l=max(l,seq[i]);            while (l<=r)            {                int mid1=l+(r-l)/3;                int mid2=l+(r-l)*2/3;                long long tmp1=calc(mid1),tmp2=calc(mid2);                if (tmp1>=tmp2)                {                    l=mid1+1;                    ans=min(ans,tmp2);                }                else                {                    r=mid2-1;                    ans=min(ans,tmp1);                }            }            return ans;        }};

Round 1C

250pts

题意简述

给出n个元素的一个数组s。定义这个数组是加法封闭的,当且仅当s[i]+s[j]也存在于这个数组。
特殊地,只含一个元素的数组也是加法封闭的。
判断这个数组是不是加法封闭的。

数据范围

1n5050si50

思路

一堆特判,没什么可说的。
数据范围这么小,随便做。
时间复杂度O(n)

代码

#include<vector>#include<string>using namespace std;class SumFullSet{    public:        string isSumFullSet(vector<int> s)        {            int n=s.size();            string yes="closed";            string no="not closed";            if (n==1)                return yes;            int pos=0,neg=0,zer=0;            int a,b;            for (int i=0;i<n;i++)                if (s[i]==0)                    zer++;                else if (s[i]>0)                    pos++,a=s[i];                else                    neg++,b=s[i];            if (pos>1||neg>1)                return no;            if (zer)                if (pos==1&&neg==1&&a==b)                    return no;                else                    return yes;            else                return no;        }}

500pts

题意简述

给出一个只包含ABC的字符串s,要求A出现间隔至少为0B出现间隔至少为1C出现间隔至少为2
判断是否可以将s重排列,使其合法,若可以,输出任意一种方案,否则输出impossible

数据范围

1len50

思路

贪心。
求出ABC当前剩余字符每种至少需要多长能放下。
每次找合法的长度最大的接到后面。
如果找不到就返回impossible
时间复杂度O(n)

代码

#include<cstdio>#include<string>using namespace std;class ThreeProgrammers{    public:        string validCodeHistory(string st)        {            string ret;            int mx,pos,len;            int num[3]={0},val[3],last[3]={-3,-3,-3};            len=st.length();            for (int i=0;i<len;i++)                num[st[i]-'A']++;            for (int i=0;i<3;i++)                val[i]=(num[i]-1)*(i+1)+1;            for (int i=0;i<len;i++)            {                mx=0,pos=-1;                for (int j=0;j<3;j++)                    if (val[j]>mx&&i-last[j]>=j+1)                        mx=val[j],pos=j;                if (pos==-1)                    return string("impossible");                last[pos]=i;                val[pos]-=pos+1;                ret+=pos+'A';            }            return ret;        }}

1000pts

题意简述

给出三个数xyk
设字符串sA的二进制表示。
L=max(1,length(s)k)
[1,x]中,有多少个A[1,x]的二进制表示中,长度不超过L的子序列,最大的所代表的十进制数y

数据范围

1x,y1012

思路

代码


Round 2A

400pts

题意简述

给出含有n个数的数列a,每个数一定是2a3b的形式。进行n1操作,每次从数列中选两个数删去,添上它们的gcdlcm,那么这样最后一定会剩下一个数。
问这个数有没有可能是x

数据范围

1n50
1ai109
1x109

思路

这题太恶心了…解释起来也好麻烦= =
进行一下模型转换,2的系数a写在左边,3的系数b写在右边,每个数就相当于是一条连线。
Possible的情况:

  1. 如果原数组本来就有x,检查其他数中a的最小值是否xab的最小值是否xb。因为我们可以先对其他所有数都取gcd,最后对这个结果与x再取一次gcd,从而得到x
    同理,检查其他数ab的最大值。
  2. 如果两个数各有一个系数和x的系数相等,并且它们同为最小或者同为最大。
    在图中表示为两条线有交叉,并且取值在同侧。
  3. 如果两个数分别有一个系数满足x,但是它们不满足同为最小或者同为最大,但是有第三个数横跨它们… 上图好了…但是只能这个方向交,不能另一个方向…因为我们可以维护两条交叉线段的同侧不变,所以这样是对的…

    其他的情况都是Impossible
    感觉说的好乱…
    时间复杂度O(n3)

代码

#include<cstdlib>#include<string>#include<vector> #include<iostream>using namespace std;#define INF 0x3f3f3f3fclass LCMGCD{    public:        struct data{            int a,b;        }d[100],need;        int n,tmp,mina,minb,maxa,maxb;        bool ok;        string isPossible(vector<int> s,int p)        {            n=s.size();            if (n==1)                if (s[0]==p)                    return string("Possible");                else                    return string("Impossible");            for (int i=0;i<n;i++)            {                tmp=s[i];                d[i].a=0;                while (tmp%2==0)                    tmp/=2,d[i].a++;                while (tmp%3==0)                    tmp/=3,d[i].b++;            }            tmp=p;            while (tmp%2==0)                tmp/=2,need.a++;            while (tmp%3==0)                tmp/=3,need.b++;            mina=minb=INF;            for (int i=0;i<n;i++)            {                if (d[i].a==need.a&&d[i].b==need.b)                {                    for (int k=0;k<n;k++)                        if (k!=i)                        {                            mina=min(mina,d[k].a);                            minb=min(minb,d[k].b);                            maxa=max(maxa,d[k].a);                            maxb=max(maxb,d[k].b);                        }                    if (mina<=need.a&&minb<=need.b||maxa>=need.a&&maxb>=need.b)                        return string("Possible");                }                for (int j=0;j<n;j++)                {                    if (i!=j&&d[i].a==need.a&&d[j].b==need.b)                    {                        if (d[i].a>d[j].a&&d[i].b<d[j].b||d[i].a<d[j].a&&d[i].b>d[j].b)                            return string("Possible");                        if (d[i].b>d[j].b)                            for (int k=0;k<n;k++)                                if (k!=i&&k!=j&&d[k].a>need.a&&d[k].b<need.b)                                    return ("Possible");                        if (d[i].b<d[j].b)                            for (int k=0;k<n;k++)                                if (k!=i&&k!=j&&d[k].a<need.a&&d[k].b>need.b)                                    return ("Possible");                    }                    if (i!=j&&d[i].b==need.b&&d[j].a==need.a)                    {                        if (d[i].a>d[j].a&&d[i].b<d[j].b||d[i].a<d[j].a&&d[i].b>d[j].b)                            return string("Possible");                        if (d[i].a<d[j].a)                            for (int k=0;k<n;k++)                                if (k!=i&&k!=j&&d[k].a>need.a&&d[k].b<need.b)                                    return ("Possible");                        if (d[i].a>d[j].a)                            for (int k=0;k<n;k++)                                if (k!=i&&k!=j&&d[k].a<need.a&&d[k].b>need.b)                                    return ("Possible");                    }                }            }            return ("Impossible");        }};

Regional Wildcard

250pts

题意简述

给出n个物品,每个物品有一个价值xi。对于每一个物品你可以选择分给A或分给B或谁都不给。
要求A,B两人每人至少分得一个物品,且两人分得物品的价值和相等,求任意一个方案,或无解。

数据范围

2n10001xi106

思路

生日悖论。
p为从给定从符合离散均匀分布的区间 [1,d] 随机取出 n 个整数, 至少2个数字相同的概率。当 n 的取值在d 左右时,p0.5;当 n 的取值在2n左右时,p已经非常接近1了。
观察到和的值域为[1109],我们可以利用生日悖论,算出>105>2n个和,看其中是否有相同的,出错概率已经非常小了。
为了进一步保证正确性,我们构造尽量多的解。可以取前25个数,算出它的所有子集的和,二进制位记录状态。
无解的情况只可能存在于n比较小的情况。
时间复杂度O(225)

代码

#include<cstdio>#include<vector>using namespace std;int ff[25000010];int final[1010];bool flag;int sz;class DivideJewelry{    public:        void dfs(int now,int sum,int tmp,vector<int> &v)        {            if (flag)                return;            if (ff[sum]&&ff[sum]!=tmp)            {                flag=true;                for (int i=0;i<25;i++)                    if (!(bool(tmp&(1<<i))^bool(ff[sum]&(1<<i))))                        final[i]=0;                    else if (bool(tmp&(1<<i)))                        final[i]=1;                    else                        final[i]=-1;                return;            }            ff[sum]=tmp;            if (now==sz)                return;            dfs(now+1,sum,tmp,v);            dfs(now+1,sum+v[now],tmp|(1<<now),v);        }        vector<int> divide(vector<int> x)        {            sz=min(int(x.size()),25);            dfs(0,0,0,x);            if (!flag)            {                vector<int> ret;                return ret;            }            else            {                vector<int> ret(final,final+int(x.size()));                return ret;            }        }};

500pts

题意简述

请你构造一个点数不超过20的无向图,这个无向图的联通子图个数为k

数据范围

1k65535

思路

ing..

代码

0 0