Codeforces Round #514 (Div. 2)

嗯,我又来水codeforces了

A. Cashier

很简单,从头到尾读入客人到达的时间,然后跟上一个客人服务结束时间进行求差,然后除以吸烟时间即可(嗯,补一句,吸烟有害健康)。注意处理初始状态和结束状态即可。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

#define MAXN 200005  //2e5

//#define TESTING

#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...) 
#endif


int main()
{
    int n,a,L;
    cin >> n >> L >> a;
    int cost[MAXN][2];
    for (int i = 0; i < n; ++i) {
        scanf("%d %d", &cost[i][0], &cost[i][1]);
    }
    int lastServe = 0;
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        int delta = cost[i][0] - lastServe;
        ans += delta / a;
        lastServe = cost[i][0] + cost[i][1];
        DEBUG("last server time :%d", lastServe);
        DEBUG("ans:%d", ans);
    }
    int delta = L - lastServe;
    ans += delta / a;
    printf("%d", ans);

    return 0;
}

B. Forgery

其实题目挺蛋疼的,说的不清楚,补充了几个说明。大致就是有一个可以写一个3*3方块的笔,这个笔不能写到纸外面,也不能写中间那个格子。而且这个笔必须每次都写3*3的格子。。。。嗯,这个笔跟我们的画风不一样。。。。直接暴力搜所有接空间即可,针对所有需要有笔迹的地方,枚举是否可以用这个笔写满。已经写过的格子可以不枚举。每次枚举需要把八个格子作为起始点进行枚举。乍看之下是8*8 * 1000 *1000的时间复杂度,担心TLE,但是实际上由于每次写的时候都是写8个格子,之后遇到写过的格子可以不枚举,所以时间复杂度是接近1000 *1000的,所以还是AC了。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

#define MAXN 200005  //2e5

#define TESTING

#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...) 
#endif


int signEmpty[1002][1003];
string sign[1005];
int n,m;
int dir[8][2] = {
    {1,1},{1,-1},{1,0},
    {0,1},{0,-1},
    {-1,-1}, {-1,1},{-1,0}};
bool check_as_central(int x,int y)
{
    for (int i = 0; i < 8; ++i) {
        if(x+dir[i][0]>=n || x+dir[i][0]<0)return false;
        if(y+dir[i][0]>=m || y+dir[i][1]<0)return false;
        if(sign[x + dir[i][0]][y+dir[i][1]] == 0)return false;
    }
    return true;
}

void mark_from_central(int x, int y)
{
    for (int i = 0; i < 8; ++i) {
        signEmpty[x+dir[i][0]][y+dir[i][1]]=1;
    }
}

bool check(int x,int y)
{
    for (int i = 0; i < 8; ++i) {
        if(check_as_central(x+dir[i][0], y+dir[i][1]))
        {
            mark_from_central(x+dir[i][0], y+dir[i][1]);
            return true;
        }
    }
    return false;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        cin >> sign[i];
    }
    for(int i = 0 ;i < n; ++i)
    {
        for (int j = 0; j < m; ++j) {
            if(sign[i][j] == '.')sign[i][j] = 0;
            else sign[i][j] = 1;
        }
    }
//    for (int i = 0; i < n; ++i) {
//        for (int j = 0; j < m; ++j) {
//            printf("%d",(int)sign[i][j]);
//        }
//        printf("\n");
//    }
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            if(sign[i][j] == 0) continue;
            if(signEmpty[i][j]==1)continue;
            if(!check(i,j))
            {
                printf("No\n");
                return 0;
            }
        }
    }
    printf("Yes\n");
    return 0;
}

C. Sequence Transformation

题目要求给出一个n,然后这个n构造一个1,2,3,4.。。。。n-1,n的n个数的数列,针对这个数列,进行n次操作,每次都是先算一个剩余全体数字的gcd,然后去除原数列里面的一个数字。每次操作得到的gcd,构造成一个序列,求怎么进行删除操作,可以构造一个字典序最大的序列。

首先,对于一个队列,如果里面有两个互质的数,那么不管这个队列有多少数字,他们的gcd总是1。

然后,不考虑更复杂的情况,只考虑简单的情况。我们看这个要怎么处理。由于队列里有互质的数字的时候,gcd总是1,两个质数总是互质,我们最先要考虑的就是怎么移除质数。以n=6为例子。1 2 3 4 5 6,如何构造字典序列最大呢?1 2 3 5这几个质数,肯定是要先去掉的,否则gcd总是1,去除1 5 都没问题,但是2和3,去除哪个呢?很明显,去除3比较合适,因为去除三之后,剩下2 4 6,可以有gcd 2。之后去除2 4,就有gcd 6了,于是有

1 1 1 2 2 6 的队列。

如果第三步那里去除2,就需要在第四步才能构造一个非1的gcd。

另外,更有趣的事情是,2 4 6,这三个数,在除以gcd 2之后,变成了1 2 3,所以这三个数的处理策略,跟处理1 2 3是一样的。

那么这个策略如何概括呢?答案就是计算最小质因子出现次数。

1,我们对前n个数,计算他们的最小质因子出现的次数分别是多少,对于i<=n,记录出现次数最多的质因子,如果出现次数相同,则记录最大的质因子。

2,对于前n个数字,我们看前n个最小质因子出现次数最多的是哪个质因子。我们可以断定,这个质因子x,就是我们当前最快能得到的非1 gcd,只需要移除没有这个质因子的数字就行了。

3,移除这些没有质因子x的数字的过程中,我们的gcd总是1,直到这些数字被移除,我们的gcd才变成x。

4,移除之后,我们将当前数字,更新为质因子x出现的次数,就是下一个队列需要处理的n,从第一步开始重复,只不过得到的结果要乘以x。

5,重复直到n为1。

我们再回过头来看n=6的情况:

1,从1到6,出现最多的质因子是2,出现了3次,(2,4,6)。然后是3,出现了一次(3)(注意,我们只计算最小质因子)。

2,所以我们得出前面3个gcd必定是1,有了队列1 1 1。然后从第四个开始,gcd为2。

3,我们把gcd 2记下来,2 4 6 退化成1 2 3。这里质因子出现的次数相同,都是1次,这个时候最大的质因子是3。

4,所以我们前面2个gcd必定是1,然后第三个gcd为3。所以1 2 3的处理结果为 1 1,这里1要跟前面的gcd 2相乘,所以有1 1 1 2 2

5,把gcd 3 记下来,3 退化成1,gcd肯定为1,这里要跟前面的2和3 相乘,得到最后的队列1 1 1 2 2 6

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

#define MAXN 200005  //2e5

//#define TESTING

#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...) 
#endif

#define MX 1000005
int prime[MX+5], factor[MX+5], input[MAXN],s[MX], ptr = 0;
int maxfactor[MX+5][2];
void init_primes_with_factor(int n)
{
    for (int i = 2; i <= n; ++i) {
        if(factor[i]== 0)
        {
            prime[ptr]=i;
            factor[i]=prime[ptr];
            ptr++;
        }
        for(int j=0; i*prime[j] <= MX;++j)
        {
            factor[i*prime[j]] = prime[j];
            if(i % prime[j] == 0)break;
        }
    }
    int maxN = 0,maxFactor = 1;
    map<int,int> factors;
    for (int j = 1; j <= n; ++j) {
        factors[factor[j]]++;
        if(factors[factor[j]] > maxN )
        {
            maxFactor = factor[j];
            maxN = factors[factor[j]];
        }
        else if(factors[factor[j]] == maxN && factor[j] > maxFactor)
        {
            maxFactor = factor[j];
            maxN = factors[factor[j]];
        }
        maxfactor[j][0] = maxFactor;
        maxfactor[j][1] = maxN;
    }
}

int main()
{
    int n = 0;
    cin >> n;
    factor[1] = 1;
    init_primes_with_factor(n);
    int ans[n];
    int ansPtr = 0;
    int baseFactor = 1;
    for (int i = n; i > 1; ) {
        int maxFactor = maxfactor[i][0];
        int maxN = maxfactor[i][1];
        for (int j = 0; j < i-maxN; ++j) {
            ans[ansPtr] = baseFactor;
            DEBUG("ans[%d]:%d",ansPtr, ans[ansPtr]);
            ansPtr++;
        }
        baseFactor *= maxFactor;
        i = maxN;
        DEBUG("maxFactor:%d, maxN:%d, baseFactor:%d, ansPtr:%d", maxFactor, maxN, baseFactor, ansPtr);
        DEBUG("i is %d",i);
    }
    ans[ansPtr++] = baseFactor;
    for (int i = 0; i < n; ++i) {
        printf("%d ", ans[i]);
    }
    printf("\n");
    return 0;
}

D. Nature Reserve

总的来说就是计算一个圆,将所有点包括进来,然后还要跟y=0相切。

ok,先来算不可能的情况,很明显,如果有两个点在河的两侧,直接gg。如果y=0上面有两个点,也gg。

然后我们来计算最大的R是多少。很显然,最大的R出现在当几个个动物分别在(-10^7,1)和(10^7, 1)(10^7,10^7),(-10^7,10^7)几个点的时候。这个时候,取x = 0,y=R做圆心,R满足:

R*R = (R-1)^2 + (10^7)^2

解方程得到R=(10^14 + 1) /2

故而这个就是R的上限了。为了方便,我们直接放大一点,将R放到10^16去算。

然后我们用二分查找法,确认R的范围即可。每次取一个R,检查是不是可以做到让所有点都在这个圆里面。

R确定后,可以肯定,点应该取在(x,R),这里只要针对每个点,计算x的取值范围,然后将所有的取值范围进行合并,最终要求合并的结果不为无结果即可。

对于确定了R的情况,一个点(x1,y1)对应的圆心取值x需要满足

R*R = (R-y1)^2 + ( x - x1)^2

用公式求解上方的x得到两个解,就是x的左右边界了。

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

#define MAXN 100005  //2e5

#define TESTING

#ifdef TESTING
#define DEBUG(S, args...) {printf(S, args);printf("\n");}
#else
#define DEBUG(S, args...) 
#endif

int n = 0;
int cord[MAXN][2];

int solve_function(long double a, long double b, long double c, long double &x1, long double &x2)
{
    int ret = 0;
    long double d = b*b - 4*a*c;
    if(d < 0) ret = 0;
    else if(d == 0)
    {
        ret = 1;
        x1 = x2 = (-b/(2.0*a));
    }
    else
    {
        ret = 2;
        long double e = sqrt(d);
        x1 = (-b + e) / (a * 2.0) ; 
        x2 = (-b - e) / (a * 2.0) ; 
    }
    return ret;
}
bool check(long double R)
{
    long double l = -1e16, r = 1e16;
    for (int i = 0; i < n; ++i) {
        long double x = cord[i][0], y = cord[i][1];
        long double a = 1.0;
        long double b = 2.0 * x;
        long double c = (x*x + y*y - 2 * y * R);
        long double x1,x2;
        int ret = solve_function(a,b,c,x1,x2);
        if(ret == 0)return false;
        if(x1 > x2)
        {
            long double tmp = x1;
            x1 = x2;
            x2 = tmp;
        }
        l = max(l, x1);
        r = min(r, x2);
        if(l > r)return false;
    }
    return true;
}

int main()
{
    cin >> n;
    bool isNegative = false, isPostive = false, isZero = false;
    for (int i = 0; i < n; ++i) {
        scanf("%d %d", &cord[i][0], &cord[i][1]);
        if(cord[i][1] < 0){isNegative = true, cord[i][1] *= -1;}
        else if(cord[i][1] > 0) isPostive = true;
        else {
            if(isZero)
            {
                printf("-1");
                return 0;
            }
            isZero = true;
        }
    }
    if(isNegative && isPostive)
    {
        printf("-1");
        return 0;
    }
    long double l = 0, r = 1e16;
    for (int i = 0; i < 255; ++i) {
        long double mid = (l + r) / 2.0;
        if(check(mid))
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
    }
    long double mid = (l + r) / 2;
    printf("%Lf", mid);
    
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值