比赛地址

Codeforces Gym

[D]Dream Team

题意

给定一个图,每一个点有点权。求边权和最大的生成树。边权的定义为两点点权的gcd值。

题解

校赛原题,具体题解见这里

话说比赛的时候直接抄校赛的代码,结果直接WA。Debug后发现校赛代码少考虑了所有点的边权全都不为1的情况,这样会少算一些答案。。。校赛数据坑死人

代码

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

    int n,m,p,i,g,T,q,flag,x;
    LL Ans;
    int v[N],f[N];

    inline int Max(int a,int b){return (a>b)?a:b;}

    inline int Find(int x){
        return (x==f[x])?x:f[x]=Find(f[x]);
    }

    int main(){
        scanf("%d",&T);
        for (g=1;g<=T;g++){
            memset(v,0,sizeof(v));  memset(f,0,sizeof(f));
            m=0;    Ans=0;
            scanf("%d",&n);
            for (i=1;i<=n;i++){
                scanf("%d",&p);
                if (p==1)   {Ans++; continue;}
                if (v[p])   {Ans+=p;    continue;}
                v[p]=1; f[p]=p; m=Max(m,p);
            }
            f[1]=1; v[1]=1;
            for (i=(m/2)+1;i;i--){
                x=i;    p=0;    q=0;    flag=0;
                while (x<=m){
                    if (!v[x])  goto END;
                    q=Find(f[x]);
                    if (flag==0){
                        flag=q; p=x;
                    }   else {
                        if (flag==q)    goto END;
                        f[q]=flag;  Ans+=(LL)i;
                    }
                    END:
                        x+=i;
                }
            }
            Ans--;
            printf("Case %d: %I64d\n",g,Ans);
        }
        return 0;
    }

[E]Evaluations

题意

给定一棵树,树上两点的距离定义为两点路径上所有边的边权之积。求有多少个无序点对(u,v),使得其距离可以分解为两个质数之积。

题解

首先把边权为1的边缩到一起,这一过程拿并查集维护。然后考虑答案的形式,要么是相邻两点,其边权正好符合题意;要么是存在相邻两条边的边权恰好为两个不同的质数。分别统计这两种答案即可。计算答案的时候,要按照缩点后的点的大小计算方案数。

计算第二种情况时,有两种思路,一种是枚举每一条边,然后枚举两个端点向外延伸出的所有边,检查是否符合题意。每条边被检查的次数和两个端点的度数相关,所以这种算法的时间复杂度是

O(n)

还有一种思路是枚举点,然后遍历延伸出的所有边,统计有多少质数边,和每种质数边有多少。每遇到一条质数边,统计一次答案即可。时间复杂度也是

O(n)

但是,实际提交的时候,第一种思路被卡常了QAQ所以赛后使用的第二种思路AC了这道题。

代码

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

int T,g,i,j,x,y,n,lsum=1,c,flag,Lsum=1;
int head[N],p[N],sum[N],vv[N],Head[N],fa[N];
LL Ans,size[N],cnt[N];
LL ss[N];

struct Data{
    int t,l;
};

struct Edge{
    int s,t,l,next;
}e[N*8],E[N*8];

vector <int> P[N],pri[N];

inline void Add(int s,int t,int l){
    e[lsum].s=s;    e[lsum].t=t;    e[lsum].l=l;    e[lsum].next=head[s];   head[s]=lsum++;
}

inline void Add2(int s,int t,int l){
    E[Lsum].s=s;    E[Lsum].t=t;    E[Lsum].l=l;    E[Lsum].next=Head[s];   Head[s]=Lsum++;
}

char buf[1<<15],*fs,*ft;

inline char gc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}

inline int gint(){
    int x=0,ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=gc();
    return x;
}

inline int Find(int x){return (x==fa[x])?x:fa[x]=Find(fa[x]);}

inline void Pre(){
    int i=0,x=0,y=0;
    for (i=2;i<=N-10;i++){
        if (vv[i])  continue;
        p[++p[0]]=i;
        for (x=i*2;x<=100000;x+=i)  pri[x].push_back(i),vv[x]=1;
    }
    for (i=6;i<=N-10;i++){
        if (pri[i].size()!=2)   continue;
        x=pri[i][0];    y=pri[i][1];
        if (x*y==i) sum[i]=1;
    }
}

inline void Ready(){
    int i=0,j=0,x=0,y=0;
    for (i=1;i<=n;i++)  size[Find(i)]++;
    for (i=1;i<=n;i++){
        x=Find(i);
        for (j=Head[i];j;j=E[j].next){
            y=Find(E[j].t);
            if (x==y)   continue;
            if (!vv[E[j].l])    Add(x,y,E[j].l);
            else if (sum[E[j].l])   Ans+=size[x]*size[y];
        }
    }
    Ans/=2;
}

inline void Work(){
    int i=0,j=0,x=0,y=0,r=0,u=0,k=0;
    LL sum=0;
    for (i=1;i<=n;i++){
        sum=0;
        for (j=head[i];j;j=e[j].next){
            x=e[j].t;   y=e[j].l;
            sum+=size[x];   cnt[y]+=size[x];
            Ans+=(sum-cnt[y])*size[x];
        }
        for (j=head[i];j;j=e[j].next)   cnt[e[j].l]-=size[e[j].t];
    }
}

int main(){
    Pre();
    T=gint();
    for (g=1;g<=T;g++){
        n=gint();
        memset(size,0,sizeof(size));
        memset(e,0,sizeof(e));  memset(head,0,sizeof(head));
        memset(E,0,sizeof(E));  memset(Head,0,sizeof(Head));
        memset(size,0,sizeof(size));
        lsum=1; Ans=0;  Lsum=1;
        for (i=1;i<n;i++){
            x=gint();   y=gint();   c=gint();
            fa[i]=i;    fa[i+1]=i+1;
            if (vv[c]&&c!=1&&!sum[c])   continue;
            Add2(x,y,c);    Add2(y,x,c);
        }
        for (i=1;i<=n;i++)
            for (j=Head[i];j;j=E[j].next)
                if (E[j].l==1)  {
                    x=Find(i);  y=Find(E[j].t);
                    if (x!=y)   fa[x]=y;
                }
        Ready();    Work();
        printf("Case %d: %I64d\n",g,Ans);
    }
    return 0;
}

[F]Forgot the Flag!

题意

给定一个凸多边形,然后每次询问会给出两个多边形内的点的坐标,求从第一个点到边界,再到第二个点的最小距离,以及选取的边界的点的坐标。

题解

根据中学知识,以每条边为镜面,第二个点对称出去,然后计算和第一个点的距离即可。

代码

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

#define IL inline
#define LL long long
#define ULL unsigned LL

#define GC getchar()

#define PC putchar
#define _Q_(x) {register char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;
#define _H_(x) for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define sc(x) _Q_(x)_H_(x)
#define se(x) _Q_(x)else if(_c==EOF)return 0;_H_(x)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define F(i,l,r) for(int i=(l);i<(r);++i)

template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define UPL(_) UPRT(_),PC(10)  
template<class T>
void PRT(const T _){if(_<0){PC(45),UPRT<ULL>(-(ULL)_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
#define PL(_) PRT(_),PC(10)


constexpr int MN(1e5+7);

const double EPS = 1e-6;
IL double ABS(double x) {return x>0 ? (x>EPS ? x:0) : (x<-EPS ? -x:0);}
IL double MIN(double a, double b) {return a<b ? a:b;}
IL double MAX(double a, double b) {return a>b ? a:b;}
IL bool eq(double a, double b) {return a>b ? a-b<EPS : b-a<EPS;}
IL bool zero(double x) {return x>0 ? x<EPS : x>-EPS;}
IL int  sign(double x) {return x>0 ? (x>EPS ? 1:0) : (x<-EPS ? -1:0);}
IL double norm(double x) {return x>0 ? (x>EPS ? x+EPS:0) : (x<-EPS ? x-EPS:0);}
void PRT(const char *fmt, double x)
{
    static char buf[666];
    sprintf(buf, fmt, norm(x));

    bool all_0 = true;
    for (char *p=buf; *p; ++p)
        if (isdigit(*p) && *p!='0')
        {
            all_0 = false;
            break;
        }
    printf("%s", buf + (all_0 && *buf=='-'));
}

#define sqr(x) ((x)*(x))
#define cub(x) ((x)*(x)*(x))

struct Po
{
    double x;
    double y;
    Po (void) { }
    Po (double xx, double yy) : x(xx), y(yy) {
    }
    void norm()
    {
        double d = sqrt(x*x + y*y);
        x /= d, y /= d;
    }
    double operator *(const Po &o) const {return x*o.x + y*o.y;}
    double operator ^(const Po &o) const {return x*o.y - y*o.x;}
    Po operator +(double d) const {return Po(x+d, y+d);}
    Po operator -(double d) const {return Po(x-d, y-d);}
    Po operator *(double d) const {return Po(x*d, y*d);}
    Po operator /(double d) const {return Po(x/d, y/d);}
    Po operator +(const Po &o) const {return Po(x+o.x, y+o.y);}
    Po operator -(const Po &o) const {return Po(x-o.x, y-o.y);}
    double operator |(const Po &o) const {return sqrt(sqr(x-o.x)+ sqr(y-o.y));}
} po[MN];

struct Li
{
    Po s, t;
    Li(void) {}
    Li(const Po &a, const Po &b) : s(a), t(b) { }
    Po get_v_dir()
    {
        Po d = t - s;
        return Po(d.y, -d.x);
    }
    double dist(const Po &p) const
    {
        return ABS((t-s) ^ (p-s) / (s | t));
    }
    Po operator &(const Li &o) const
    {
        const double p1 = (o.t-o.s) ^ (s-o.s);
        const double p2 = (o.t-o.s) ^ (t-o.s);
        return Po((s.x*p2 - t.x*p1)/(p2-p1), (s.y*p2 - t.y*p1)/(p2-p1));
    }
};

struct Ans
{
    double len;
    Po c;
};


int main()
{
    int T;
    sc(T)
    Po p1, p2, v, mid, mirror;
    Li l;
    Ans ans, cans;
    for (int cas=1; cas<=T; ++cas)
    {
        printf("Case %d:\n", cas);
        get(n)
        F(i, 0, n)
            scanf("%lf %lf", &po[i].x, &po[i].y);

        get(q)
        while (q--)
        {
            scanf("%lf %lf %lf %lf", &p1.x, &p1.y, &p2.x, &p2.y);
            po[n] = po[0];
            ans.len = 1e15;
            for (int i=0; i<n; ++i)
            {
                l.s = po[i], l.t = po[i+1];
                v = l.get_v_dir();
                v.norm();
                v = v * l.dist(p1);

                mirror = p1 + v + v;
                if (sign((l.s-p1) ^ (l.t-p1)) == sign((l.s-mirror) ^ (l.t-mirror)))
                {
                    mid = p1 - v;
                    mirror = mid - v;
                }

                cans.len = p2 | mirror;
                cans.c = Li(mirror, p2) & l;
                if (cans.len < ans.len)
                    ans = cans;
            }
//          PRT("%.7f ", ans.len);
//          PRT("%.7f ", ans.c.x);
//          PRT("%.7f\n", ans.c.y);
            printf("%.7f %.7f %.7f\n", ans.len, ans.c.x, ans.c.y);
        }
    }

    return 0;
}

[G]Glorious Stadium

题意

给出两个整数k和n。每一次画出一个正k边形,然后画出其内接圆,要求该内接圆是上一个正多边形的外接圆。并给出第一个内接圆的半径R。每一个正多边形的面积减去其内接圆的面积。求所有有效面积之和。

题解

签到题

代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cmath>

    using namespace std;

    double mi(double a, int n) {
        if (n == 0) return 1;
        if (n == 1) return a;
        double m = mi(a, n/2);
        m = m * m;
        if (n%2) m = m * a;
        return m;
    }

    double nei1, nei2, bili1, bili2, s1, s2, K, duibian, s, sk;
    int T, n, r, k;
    int main() {
        double pi = acos(-1);
        cin >> T;
        for (int t = 1; t <= T; t++) {
            scanf("%d%d%d", &n, &r, &k);
            nei1 = pi/k;
            bili1 = cos(nei1);
            K = 1.0/bili1/bili1;

            duibian = tan(nei1);
            s1 = duibian/2;
            s2 = nei1/2;
            bili2 = (s1-s2)/s2;
            sk = pi * r * r;
            if (n == 1) s = sk;
            else s = (mi(K, n) - 1)/(K-1) * sk;
            printf("Case %d: %.5lf\n", t, s*bili2);
        }
        return 0;
    }

[H]Half Nice Years

题意

给定一个范围[L,R],求该范围内的最大十进制整数,使得该整数从正中间 分为两部分后(长度若为奇数则分为k和k+1两部分),两段的gcd大于1。

题解

代码

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

    using namespace std;

    int p[1000005];
    int anss[10005], pan[10005];

    int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a%b);
    }

    int s[15];
    bool ok(int n) {
        int k = n;
        int wei = 0;
        while(k) { s[++wei] = k%10; k /= 10; }
        int a = 0, b = 0;
        if (wei % 2 == 0) {
            for (int i = wei; i > wei/2; i--) a = a*10 + s[i];
            for (int i = wei/2; i >= 1; i--) b = b*10 + s[i];
        } else {
            for (int i = wei; i > wei/2+1; i--) a = a*10 + s[i];
            for (int i = wei/2+1; i >= 1; i--) b = b*10 + s[i];
        }
        if (a == 0 || b == 0) return false;
        if (gcd(a, b) > 1) return true;
        return false;
    }

    void st() {
        p[1] = 1;
        for (int i = 2; i <= 1000000; i++) {
            if (p[i] == 0)
                for (int j = i; j <= 1000000/i; j++) if (p[i*j] == 0) p[i*j] = i;
        }
        for (int i = 10; i <= 10000; i++) if (ok(i)) pan[i] = 1;
        int now = 0;
        for (int i = 10; i <= 10000; i++) {
            if (pan[i]) now = i;
            anss[i] = now;
        }
    }

    long long int max(long long int a, long long int b) {
        if (a > b) return a;
        return b;
    }

    long long int find(long long int n) {
        if (n <= 10000) return 1LL * anss[n];
        long long int k = n, ans = 0;
        int wei = 0;
        while(k) { s[++wei] = k%10; k /= 10; }
        int a = 0, b = 0, bili = 1, A;
        if (wei % 2 == 0) {
            for (int i = wei; i > wei/2; i--) a = a*10 + s[i];
            for (int i = wei/2; i >= 1; i--) b = b*10 + s[i];
            for (int i = wei/2; i >= 1; i--) bili *= 10;
        } else {
            for (int i = wei; i > wei/2+1; i--) a = a*10 + s[i];
            for (int i = wei/2+1; i >= 1; i--) b = b*10 + s[i];
            for (int i = wei/2+1; i >= 1; i--) bili *= 10;
        }
        A = a;
        while(A > 1) {
            int c = p[A];
            if (c == 0) c = A;
            if (b - b%c > 0) ans = max(ans, 1LL*a*bili+b-b%c);
            A /= c;
        }
        A = a-1; b = bili - 1;
        while(A > 1) {
            int c = p[A];
            if (c == 0) c = A;
            if (b - b%c > 0) ans = max(ans, 1LL*(a-1)*bili+b-b%c);
            A /= c;
        }
        return ans;
    }

    int T;
    long long int ans, a, b;
    int main() {
        st();
        cin >> T;
        for (int t = 1; t <= T; t++) {
            int flag = 1;
            scanf("%I64d%I64d", &a, &b);
            if (b <= 21) flag = 0;
            if (flag) ans = find(b);
            if (flag && ans == 0 && b > a) ans = find(b-1);
            if (ans < a) flag = 0;
            if (flag) printf("Case %d: %I64d\n", t, ans); 
            else printf("Case %d: impossible\n", t, ans); 
        }
        return 0;
    }

[J]Jacked Tickets

题意

给定两个整数n和m,现需要把整数n分为m份,所有份总和为n。大小为k的份数有一个损失P(k),P(k)是凹函数。求最小损失。

题解

签到题

代码

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

    using namespace std;

    int n, a, b, T;
    long long int p[505];

    int main() {
        cin >> T;
        for (int t = 1; t <= T; t++) {
            printf("Case %d:\n", t);
            scanf("%d", &n);
            for (int i = 1; i <= n; i++) scanf("%I64d", &p[i]);
            scanf("%d", &n);
            for (int i = 1; i <= n; i++) {
                scanf("%d%d", &a, &b);
                if (b > a) printf("impossible\n");
                else {
                    long long int ans = 0;
                    int k = a/b;
                    int s = a%b;
                    for (int i = 1; i <= s; i++) ans += p[k+1];
                    for (int i = s+1; i <= b; i++) ans += p[k];
                    printf("%I64d\n", ans);
                }
            }
        }
        return 0;
    }

[K]Katryoshka

题意

给定三种组件A,B,C。有三种商品,每制造一件商品,消耗的组件数量分别是(2,0,1),(2,1,1),(1,1,1).求最多能造多少商品。

题解

签到题

代码

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

    int n,m,k,i,T,s1,s2;

    inline int Abs(int x){return (x<0)?-x:x;}
    inline void Swap(int &a,int &b){a^=b^=a^=b;}
    inline int Min(int a,int b){return (a<b)?a:b;}
    inline int Max(int a,int 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("%d",&T);
        for (i=1;i<=T;i++){
            scanf("%d%d%d",&n,&m,&k);
            s1=s2=0;
            if (m>=n)   s1=n;   else s1=m+(n-m)/2;
            s2=n/2; if ((n%2)&&m)   s2++;
            printf("Case %d: %d\n",i,Min(Max(s1,s2),k));
        }
        return 0;
    }

[L]Lazy ERCD

题意

有n支队伍参加比赛,每进行一轮比赛会淘汰一支队伍,求需要几场比赛能确定冠军队伍。

题解

签到题

代码

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

int T,n,i;


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 int Max(int a,int 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("%d",&T);
    for (i=1;i<=T;i++){
        scanf("%d",&n);
        printf("Case %d: %d\n",i,n-1);
    }
    return 0;
}

总结&吐槽

这场比赛算是最近几场里面稍微简单一些的了,以中低档题为主。E题在比赛中一直被卡常,I题写的二维莫队也被卡常了(正解CDQ分治)。如果能够做出来就是9题了QAQ。话说第一次见这么丧心病狂的套题,非要卡常,输出还必须格式化

目前训练已经进行了四场了,目前的积分在第九名,差一点可以进队。希望接下来可以好好发挥。

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