Codeforces Round 503

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/81605794

A:Elections
枚举其他党的最大票数再算至少花费多少超过这个值,理论上能够做到O(n)
AC code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#define maxn 3005
#define LL long long
using namespace std;

template <class T> inline void read(T &res) // non - negative
{
    char ch;
    for(;!isdigit(ch=getchar()););
    for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}

int n , m , id[maxn] , val[maxn] , pre[maxn] , nxt[maxn] , loc[maxn] , siz[maxn];
vector<int>G[maxn];
inline bool cmp(const int &a,const int &b){ return val[a] > val[b]; }
inline bool cmp2(const int &a,const int &b){ return val[a] < val[b]; }
LL sum1,sum2,had,ans=1ll<<60;

inline void Del(int now)
{
    nxt[pre[now]] = nxt[now];
    pre[nxt[now]] = pre[now];
}

int main()
{
    read(n),read(m);
    for(int i=1,u,v;i<=n;i++)
    {
        scanf("%d%d",&u,&val[i]);
        if(u==1) val[i] = 0;
        G[u].push_back(i);
        siz[u]++;
        id[i] = i;
    }

    sort(id+1,id+1+n,cmp2);
    for(int i=1;i<=n;i++) loc[id[i]] = i , pre[i] = i-1 , nxt[i] = i+1;
    for(int i=1;i<=m;i++) sort(G[i].begin() , G[i].end() , cmp);
    nxt[0] = 1;

    for(int i=n , j ,tim;i>=0;i--)
    {
        for(j=1;j<=m;j++)
            for(;siz[j] > i;)
                sum1 += val[G[j][siz[j]-1]] , Del(loc[G[j][siz[j]-1]]) , siz[j]-- , had++;

        sum2=0;
        for(j=nxt[0],tim=had;tim<=i && j<=n;tim++,j=nxt[j])
            sum2 += val[id[j]];

        if(tim > i)
            ans = min(ans , sum1+sum2);
    }
    printf("%lld",ans);
}

B - The hat
先算出1和n/2+1的值v(1)和v(n/2+1)
环分成两条链,v1v2~v(n/2+1) 和 v(n/2+1)v(n/2+2)~v(1)
假设v(1) < v(n/2+1) 反之同理
设f(x) 为 第一条链上距离第一条链的起点(1)距离为x的数的值,g(x) 为第二条链上距离第二条链起点(n/2+1)距离为x的数的值。
发现f(0) < g(0) , f(n/2) > g(n/2),如果(g(0) - f(0)) % 2 == 0,那么这两个函数必有交点
可以二分交点位置
AC Code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#define maxn 3005
#define LL long long
using namespace std;

template <class T> inline void read(T &res) // non - negative
{
    char ch;
    for(;!isdigit(ch=getchar()););
    for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}

int n;
int Ask(int k)
{
    int ret;
    printf("? %d\n",k);
    fflush(stdout);
    scanf("%d",&ret);
    return ret;
}

int main()
{
    read(n);
    int a= Ask(1) , b = Ask(n / 2 + 1);
    if(a==b){ printf("! %d\n",1);fflush(stdout);return 0; }
    else if((a - b) & 1){ puts("! -1");fflush(stdout);return 0; }
    else if(a < b)
    {
        int L = 2 , R = n/2, Mid;
        for(;L<R;)
        {
            Mid=(L+R) >> 1;
            a = Ask(Mid) , b = Ask(Mid + n/2);
            if(a < b) L = Mid + 1;
            else R = Mid;
        }
        printf("! %d\n",L);
    }
    else
    {
        int L = 2 , R = n/2, Mid;
        for(;L<R;)
        {
            Mid=(L+R) >> 1;
            a = Ask(Mid) , b = Ask(Mid + n/2);
            if(a > b) L = Mid + 1;
            else R = Mid;
        }
        printf("! %d\n",L);
    }
    return 0;
}

C. Sergey’s problem
对于一个有向图,求出一个独立集,使得图中每一个点都可以被这个独立集的点走两步走到。
如果是一个DAG,那么问题十分简单,从上至下贪心即可。
如果有环,那么我们把环中的一些点删去使得其变成一个DAG,可以证明,按照之前DAG的贪心方案可以走两步覆盖被删去的点,那么为了构造DAG,我们定义一个偏序关系(比如编号从小到大),只有大的才能往小的连边,如果小的往大的连边就有可能构成环,我们就把大的点删去。
AC code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define maxn 1000006
using namespace std;

inline void read(int &res){ char ch;for(;!isdigit(ch=getchar()););for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); }
int n,m,typ[maxn],deg[maxn]; // 1类节点:拓扑图上的节点。2类节点:被忽略的节点
int info[maxn] , Prev[maxn * 2] , to[maxn * 2] , cnt_e;//second
inline void Node(const int &u,const int &v){ Prev[++cnt_e] = info[u] , info[u] = cnt_e , to[cnt_e] = v;}
bool ban[maxn];
int pt[maxn] , cnt;

void ser(int now)
{
    if(deg[now] || typ[now]!=1) return;
    deg[now] = -1;
    if(!ban[now]) pt[++cnt] = now;
    for(int i=info[now];i;i=Prev[i])
    {
        deg[to[i]]--;
        if(!ban[now]) ban[to[i]] = 1;
        ser(to[i]);
    }
}

int main()
{
    read(n),read(m);
    for(int i=1,u,v;i<=m;i++) read(u),read(v),Node(u,v);
    for(int i=1;i<=n;i++)
        if(!typ[i])
        {
            typ[i] = 1;
            for(int j=info[i];j;j=Prev[j])
                if(typ[to[j]]) deg[to[j]]++;
                else typ[to[j]] = 2;
        }

   for(int i=1;i<=n;i++) ser(i);
   printf("%d\n",cnt);
   for(int i=1;i<cnt;i++) printf("%d ",pt[i]);
   printf("%d\n",pt[cnt]);
}

D:
先介绍一个暴力算法,将每个点坐标Hash,然后按Hash值枚举
然后,就过了,当然,会有无良人士hack你,这样就多找几个质数然后按照一些规则选择最好的质数。(欣赏大佬的代码时get到的)(然而我是补题的,所以没有多找几个素数)
AC Code:

By Fister, contest: Codeforces Round #503 (by SIS, Div. 1), problem: (D) Large Triangle, Accepted, #
 #include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<stdlib.h>
#include<vector>
#define maxn 2005
#define LL long long
using namespace std;

int siz[40][40],inv[40][40];
vector<int>idx[40][40];
template<class T>inline void read(T &res){ char ch;bool flag=0;for(;!isdigit(ch=getchar());)if(ch=='-')flag = 1;for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');(flag) && (res=-res); }
LL n,S,modS,x[maxn],y[maxn],p[6] = {37};

inline void check(const int &x1,const int &y1,const int &x2,const int &y2,const int &x3,const int &y3)
{
    for(int i=0;i<siz[x1][y1];i++)
        for(int j=0;j<siz[x2][y2];j++)
            for(int k=0;k<siz[x3][y3];k++)
                if(abs((x[idx[x1][y1][i]] - x[idx[x2][y2][j]]) * (y[idx[x3][y3][k]] - y[idx[x2][y2][j]]) - (x[idx[x3][y3][k]] - x[idx[x2][y2][j]]) * (y[idx[x1][y1][i]] - y[idx[x2][y2][j]])) == S)
                {
                    puts("Yes");
                    printf("%lld %lld\n%lld %lld\n%lld %lld\n",x[idx[x1][y1][i]],y[idx[x1][y1][i]],x[idx[x2][y2][j]],y[idx[x2][y2][j]],x[idx[x3][y3][k]],y[idx[x3][y3][k]]);
                    exit(0);
                }
}

int main()
{
    read(n),read(S);S*=2;
    for(int i=0;i<n;i++)read(x[i]),read(y[i]);
    LL Min=1ll<<60 , P=-1;
    for(int j=0;j<1;j++)
    {
        LL tmp = 0;
        memset(siz,0,sizeof siz);
        for(int i=0;i<n;i++)
            siz[(x[i]%p[j]+p[j])%p[j]][(y[i]%p[j]+p[j])%p[j]]++;
        for(int i=0;i<p[j];i++)
            for(int k=0;k<p[j];k++)
                tmp = tmp + 1ll * siz[i][k] * siz[i][k];
        if(tmp < Min) Min = tmp , P = j;
    }
    P = p[P];
    modS = S % P;
    memset(siz,0,sizeof siz);
    for(int i=0;i<n;i++)
        siz[(x[i]%P+P)%P][(y[i]%P+P)%P]++ , idx[(x[i]%P+P)%P][(y[i]%P+P)%P] . push_back(i);
    for(int i=0;i<P;i++)
        for(int j=0;j<P;j++)
        {
            int k = i * j % P;
            inv[k][i] = j;
        }

    for(int x1=0;x1<P;x1++)
        for(int y1=0;y1<P;y1++) if(siz[x1][y1])
            for(int x2=x1;x2<P;x2++)
                for(int y2=0;y2<P;y2++) if(siz[x2][y2])
                    for(int x3=x1;x3<P;x3++)
                    {
                        int y3 = (inv[((modS+1ll*(x3-x2)*(y1-y2))%P+P)%P][(x1-x2+P)%P]+y2)%P;
                        if(siz[x3][y3])
                            check(x1,y1,x2,y2,x3,y3);
                    }
    puts("No");
}

正解 :
枚举一条边然后考虑快速算出最优的第3个点。
发现每换一条边的时候好像很难维护啊,很难维护啊,难维护啊,维护啊,护啊,啊
真的很难维护吗 ?
对于两个点u,v,两个点之间的直线斜率为k1,假设这两个点都在这条直线(斜率为k2)的上方,u在v的左侧,那么只有k2 < k1时才会hv > hu ,k2 > k1 则hu > hv.
那么两个点的优劣关系只有在询问斜率超过这两个点的斜率时才会交换,我们可以从小到大枚举斜率,当某个点对的斜率被超过时,交换其优劣位置,O(1)维护………..
自闭到不想打代码

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/81605794