比赛地址

Codeorces Gym

[A]Anagrams

题意

定义x,y是b同构的,当且仅当x,y在b进制下的位数相同(无前导0),而且可以通过交换数字顺序将x变成y.

给出数字b,求出有多少个k满足,当k整除x时,看也整除x在b进制下的所有同构数字.

题解

签到题

代码

#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>

constexpr int MN(1e6+7);

int main()
{
    int n;
    scanf("%d", &n);
    --n;
    std::vector<int> vec;
    int sq = sqrt(n) + 2;
    for (int i=1; i<sq; ++i)
        if (n%i == 0)
            vec.emplace_back(i), vec.emplace_back(n/i);
    std::sort(vec.begin(), vec.end());
    vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
    for (auto e : vec)
        printf("%d ", e);

    return 0;
}

[C]Colder-Hotter

题意

交互题.

在二维平面上有一个终点,现在要通过一系列询问确定这个终点的坐标.每次询问输出一个坐标,当该位置比上一个位置更接近终点时,则输出1;当是第一次询问或者没有上一次更接近时,输出0.询问 次数不能超过500次.在这之前要输出终点的坐标.

题解

二分答案.考虑到询问次数的上界较高,而且单纯判断距离较为复杂,所以每次询问格式为(0,y)或者(x,0).通过两次二分确定横纵坐标即可.

代码

    #include<cstring>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<vector>
    #include<cmath>
    #include<map>
    using namespace std;

    int main(){

        long long int x = 0, y = 1e9;
        long long int ansx, ansy;
        int a;

        while(y > x) {

            int k = (x+y)%2;
            int mid = (x+y)>>1;

            printf("0 %I64d\n", x);
            fflush(stdout);
            scanf("%d", &a);
            printf("0 %I64d\n", y);
            fflush(stdout);
            scanf("%d", &a);

            if (a) x = mid + 1;
            else y = mid;

        }

        ansy = x;

        x = 0, y = 1e9;

        while(y > x) {

            int k = (x+y)%2;
            int mid = (x+y)>>1;

            printf("%I64d 0\n", x);
            fflush(stdout);
            scanf("%d", &a);
            printf("%I64d 0\n", y);
            fflush(stdout);
            scanf("%d", &a);

            if (a) x = mid + 1;
            else y = mid;

        }
        ansx = x;
        printf("A %I64d %I64d", ansx, ansy);    
        return 0;
    }

[D]Delay Time

题意

为测量重力加速度,做了两次实验,已知这两次实验的下落时间和下落高度.但是由于秒表不准,每次测量的时间会和实际时间有一个固定的偏差.求这个偏差是多少.

题解

签到题

代码

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;

double h1,h2,t1,t2,x,g,t;

inline int Abs(int x){return (x<0)?-x:x;}
inline void Swap(int &a,int &b){a^=b^=a^=b;}
inline LL Min(LL a,LL b){return (a<b)?a:b;}
inline LL Max(LL a,LL b){return (a>b)?a:b;}

inline int read(){
    int p=0;    char    c=getchar();
    while (c<48||c>57)  c=getchar();
    while (c>=48&&c<=57)    p=(p<<1)+(p<<3)+c-48,c=getchar();
    return p;
}

int main(){
    scanf("%lf%lf",&h1,&t1);
    scanf("%lf%lf",&h2,&t2);
    x=sqrt(h1/h2);
    t=(t1-x*t2)/(x-1);
    g=2*h1/(t1+t)/(t1+t);
    printf("%.10lf\n",-t);
    //cout<<t<<endl;
    return 0;
}

[G]

题意

在二维平面上分布着一些游客.现在要选定一个位置,让所有的游客都集中到这里.位置的选取是随机的,且只能选取坐标上有游客的位置.游客在向目标移动时,可以沿方格走直线或者走对角线.求选择哪个位置,使得最后一个到达的游客所用时间最长.

题解

可以直接构造曼哈顿距离最小生成树求解.改造一下即可求该题意下的最远点对.

下面的做法是直接暴力求凸包,然后旋转卡壳,求最远两点,直接输出...居然过了...

代码

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define IL

const int maxn = 200000 + 7;
const double sqrt2 = sqrt(2);

const double eps = 1e-8;
const double pi = acos(-1.0);
struct CPoint
{
    double x, y;
    int id;
} pt[maxn];

template <class T>
IL T min(T x, T y)
{
    return x < y ? x : y;
}
template <class T>
IL T max(T x, T y)
{
    return x > y ? x : y;
}
IL double sqr(double x)
{
    return x * x;
}
IL int dcmp(double x)
{
    if (x<-eps) return -1;
    else return (x>eps);
}
IL double cross(CPoint p0, CPoint p1, CPoint p2)
{
    return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}
IL double dot(CPoint p0, CPoint p1, CPoint p2)
{
    return (p1.x - p0.x)*(p2.x - p0.x) + (p1.y - p0.y)*(p2.y - p0.y);
}
IL double dissqr(CPoint p1, CPoint p2)
{
    return sqr(p1.x - p2.x) + sqr(p1.y - p2.y);
}
IL double dis(CPoint p1, CPoint p2)
{
    double dx = p1.x - p2.x, dy = p1.y - p2.y;
    if (dx < 0) dx = -dx;
    if (dy < 0) dy = -dy;
    return dx < dy ? sqrt2*dx + dy - dx : sqrt2*dy + dx - dy;
//  return sqrt(sqr(p1.x - p2.x) + sqr(p1.y - p2.y));
}
IL int PointEqual(const CPoint &p1, const CPoint &p2)
{
    return dcmp(p1.x - p2.x) == 0 && dcmp(p1.y - p2.y) == 0;
}


CPoint bp; // for polar sorting
int PolarCmp(const CPoint &p1, const CPoint &p2)
{
    int u = dcmp(cross(bp, p1, p2));
    return u>0 || (u == 0 && dcmp(dissqr(bp, p1) - dissqr(bp, p2)) <0);
}

void convex(CPoint pin[], int n, CPoint ch[], int &m)
{
    int i, j, k, u, v;
    memcpy(ch, pin, n * sizeof(CPoint));
    for (i = k = 0; i<n; i++)
    {
        u = dcmp(ch[i].x - ch[k].x);
        v = dcmp(ch[i].y - ch[k].y);
        if (v<0 || (v == 0 && u<0)) k = i;
    }
    bp = ch[k];
    std::sort(ch, ch + n, PolarCmp);
    n = std::unique(ch, ch + n, PointEqual) - ch;
    if (n <= 1)
    {
        m = n;
        return;
    }
    if (dcmp(cross(ch[0], ch[1], ch[n - 1])) == 0)
    {
        m = 2;
        ch[1] = ch[n - 1];
        return;
    }
    ch[n++] = ch[0];
    for (i = 1, j = 2; j<n; j++)
    {
        while (i >0 && dcmp(cross(ch[i - 1], ch[i], ch[j])) <= 0) i--;
        ch[++i] = ch[j];
    }
    m = i;
}

struct Ans
{
    int i1, i2;
    double d;

    Ans(int x, int y, double dd) :
        i1(x), i2(y), d(dd)
    {
    }

    bool operator <(const Ans &o) const
    {
        return d<o.d;
    }
    bool operator >(const Ans &o) const
    {
        return d>o.d;
    }
};

Ans Diameter(CPoint *p, int n)
{
    convex(p, n, p, n);
    if (n == 2) return Ans(p[0].id, p[1].id, dis(p[0], p[1]));
    int u, nu, v, nv, k;
    Ans ret(-1, -1, -1);
    p[n] = p[0];
    for (u = 0, v = 1; u<n; u = nu)
    {
        nu = u + 1;
        while (1)
        {
            nv = (v + 1) % n;
            k = dcmp((p[nu].x - p[u].x) * (p[nv].y - p[v].y) - (p[nv].x - p[v].x) * (p[nu].y - p[u].y));
            if (k <= 0) break;
            v = nv;
        }
        ret = max(ret, Ans(p[u].id, p[v].id, dis(p[u], p[v])));
        if (k == 0) ret = max(ret, Ans(p[u].id, p[nv].id, dis(p[u], p[nv])));
    }
    return ret;
}


int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i)
        scanf("%lf %lf", &pt[i].x, &pt[i].y), pt[i].id = i+1;
    Ans ss = Diameter(pt, n);
#ifdef _KEVIN
    printf("%d %d %lf", ss.i1, ss.i2, ss.d);
#else
    printf("%d %d", ss.i1, ss.i2);
#endif
}

[H]Hashing

题意

给出一个整数序列,每个数不超过255.现在需要从其中选出一个子列,使下面的式子取值最大,并求出最大值.

Hash=\sum_{i=0}^{k}a_{i} \ \ xor \ \ i

题解

很经典的DP问题.

考虑到当选择的子列长度过大时,只有二进制后8位才会对答案造成影响.然后盲猜有下面的结论:序列长度过大时,一定会选择长度接近原序列的子列.于是DP状态就可以进行压缩,不嫩得到以下DP方程:

dp[i][j]=Max(dp[k][j-1]+a[i] \ xor \ (j-1)) \ 0 \leq j \leq 255

前一个极大值可以用另一个数组维护,所以总的时间复杂度为

O(255n)

代码

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define N 100010
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;

int n,i;
int a[N];
LL W[550];
LL dp[N][513];

inline int Abs(int x){return (x<0)?-x:x;}
inline void Swap(int &x,int &y){x^=y^=x^=y;}
inline int Min(int a,int b){return (a<b)?a:b;}
inline LL Max(LL a,LL b){return (a>b)?a:b;}

inline int read(){
    int p=0;    char    c=getchar();
    while (c<48||c>57)  c=getchar();
    while (c>=48&&c<=57)    p=(p<<1)+(p<<3)+c-48,c=getchar();
    return p;
}

inline void Work2(){
    int i=0,j=0,x=0,y=0;
    LL Ans=0;
    memset(W,-1,sizeof(W));
    memset(dp,-1,sizeof(dp));
    W[255]=0;
    for (i=1;i<=n;i++){
        for (j=0;j<=255;j++){
            x=j?j-1:255;
            if (W[x]<0) continue;
            y=i/256;
            if (y*256+j>i-1)    y--;
            dp[i][j]=W[x]+(a[i]^(y*256+j));
            Ans=Max(Ans,dp[i][j]);
        }
        for (j=0;j<=255;j++)    W[j]=Max(W[j],dp[i][j]);
    }
    cout<<Ans<<endl;
}

int main(){
    scanf("%d",&n);
    for (i=1;i<=n;i++)
        scanf("%X",&a[i]);
    Work();
    return 0;
}

[I]Illegal or Not

题意

给定签证的停留天数认证规则,求在一些时间段内是否违反了这一规定.

题解

签到题

代码

#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>

constexpr int MN(18260);
bool vis[MN];

int main()
{
    int n;
    scanf("%d", &n);
    for (int i=0; i<n; ++i)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        for (int t=l; t<=r; ++t)
            vis[t] = true;
    }

    bool ok = true;
    for (int l=1; l<=1826; ++l)
    {
        int cnt = 0;
        for (int i=0; i<180; ++i)
            cnt += vis[l+i];
        if (cnt > 90)
        {
            ok = false;
            break;
        }
    }

    puts( ok ? "Yes" : "No");

    return 0;
}

[K]King's Rout

题意

有n个人,从1~n编号,现在需要对这n个人排序,要求是先使1号尽可能靠前,然后是2号,以此类推.此外,有m条限制规则,表述是i号必须在j号前面.求排序方案.

题解

根据限制规则反向连边,然后拓扑排序.排序的时候,优先选取编号大的入队.最后将入队顺序倒叙输出即可.

代码

#include <iostream>
#include <cstdio>
#include <queue>

constexpr int MV(2e5+7);
constexpr int ME(4e5+7);


int ind[MV];
std::vector<int> G[MV];
std::priority_queue<int> q;

int sta[MV], top;
void ts(int V)
{
    for (int u=1; u<=V; ++u)
        if (!ind[u])
            q.push(u);

    while (!q.empty())
    {
        int u = q.top();
        q.pop();
        sta[top++] = u;
        for (auto v: G[u])
            if (--ind[v] == 0)
                q.push(v);
    }
}

int main()
{
    int V, E;
    scanf("%d %d", &V, &E);
    int u, v;
    while (E--)
        scanf("%d%d", &u, &v), G[v].push_back(u), ++ind[u];
    ts(V);
    while (top--)
        printf("%d ", sta[top]);
    return 0;
}

总结&吐槽

这场比赛题目难度中等,思维性较强.一开始K题敲定为拓扑排序,但是拓扑的方式被Hack掉了;中间思路一度被带偏了老远.过了很长一段时间才想出正解.G题没有什么好的方法,抱着试一试的心态敲了一个凸包加旋转卡壳,没想到这数据直接给过了QAQ中间H题的DP一度被我口胡出了正解,可能是写法上的问题,我是对512取的模,目的是压常数.最后发现正解也利用了当初猜的一个结论.

之后的比赛碰见思维题还是要大胆一点,果断猜结论.这场中间基本一直都有人在敲键盘,时间分配还算合理.希望之后不会再出现在大众题上卡顿的情况了.

说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...