1. 程式人生 > >CodeForces CF #508 Div.2

CodeForces CF #508 Div.2

A. Equality
http://codeforces.com/contest/1038/problem/A

hash,min,沒有其他內容了,滴,簽到完成

B. Non-Coprime Partition
http://codeforces.com/contest/1038/problem/B

給你一個數n,希望你把它分成兩組,滿足兩組數各自的和的GCD>1。

先看資料規模,45000,基本上是不能超過O(NlogN)的。但是這個題目,怎麼看怎麼是數學題。我覺得必定有思維上O(1)的解法。

猜測要麼在前面取要麼在後面取。前面沒有特徵性,不行。那麼我們取n放進第一組,其他所有數放進第二組。

嘗試幾組資料,好像都成立,比賽的時候閉著眼睛往上寫就完事了,證明能吃嗎?當然,現在是賽後,以下給出證明。

首先,對於1,2,窮舉證明無解。當大於等於3時:

對於一個數n>=3(n∈N*),分成上述兩組,那麼sum1=n,sum2=1+2+…+(n-1)=n*(n-1)/2。

①若n為奇數,那麼(n-1)為偶數,sum2=n*((n-1)/2),也就是說GCD(sum1,sum2)>=n>1

②若n為偶數,那麼n=2*(n/2)且n/2>1(因為n>2),同時,sum2=(n-1)*(n/2),所以GCD(sum1,sum2)=n/2>1

綜上所述,如此分組對任意n>=3(n∈N*)都成立。

#include<iostream>
using namespace std;

int main()
{
    int n;
    cin>>n;
    if(n==1||n==2)cout<<"No"<<endl;
    else
    {
        cout<<"Yes"<<endl;
        cout<<1<<" "<<n<<endl;
        cout<<n-1;
        for(int i=1;i<n;i++)cout
<<" "<<i; cout<<endl; } }

複雜度為O(N)

C. Gambling
http://codeforces.com/contest/1038/problem/C

博弈題。有兩個人,各有一個長度為n的waiting list。每次行動可以選擇殺死一個自己list的中的數獲得其數值,或者殺死對方list的中的一個數並且不獲得任何收益。兩人輪流行動,若兩人都是理性人,希望(自己的數值-對方的數字)最大,問最終結果如何?

先看資料規模1e5,上限O(NlogN)。首先不難發現,不管選取對方的還是選取自己的,總是會選取list中最大的一個,這一點非常顯然。那麼無論如何,一個sort總是要的,這個複雜度正好對上,剩下的就要靠靈性做題了。

我們用高中物理中常用的整體法和部分法來分析:首先,兩個list中的元素的總和分別是固定的,所以在只選取自己的數的情況下,兩者會有一個確定的結果,有一個可能不為零的差。而如果兩個人都只殺死對方的數,那麼兩個人的結果都將是0。

一種玄學的解釋是,對於佔優勢(總和較大)的人來說,可能會更傾向於選擇自己的;反之亦然。那麼這就意味著,選擇殺死對方的數,本質上是一個降低差距的過程;而選擇自己的數,本質上是一個維持本來差距(也就是上一段求的和)的過程。所以在當前回合劣勢(目前最大的數在對方那裡)時,選擇對方的數,反之選擇自己的。

而更數學的證明如下。

對於給定的兩組數,它們的和分別為定值SUM1,SUM2。對於玩家A,他可以在其兩個桶中總共選擇n個數,其中:

從SUM1中選擇的數的合記為A1,從SUM2中選擇的數的和記為A2。
同樣的,玩家B分別選到了B1和B2。

因為所有的數都是被A,B瓜分了,所以有:

SUM1=A1+A2(1)
SUM2=B1+B2(2)

玩家A的最終得分是A1(從SUM1中選到的),玩家B 最終得分是B2。
玩家A看到的差值delta=A1-B2(3)

將(1)(2)帶入(3)中得到:

delta=A1-(SUM2-B1)=A1+B1-SUM2

其中A1+B1為玩家A選擇的所有的數的總和。

所以說白了,A希望選擇這2*n個數裡面較大的n個數,B也一樣。那貪心就完事了。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn=2e5+7;
int n;
struct g
{
    int w;
    int ind;
}a[maxn];
bool operator < (g x,g y){return x.w>y.w;}
long long ans=0;

int main()
{
    cin>>n;
    for(int i=0;i<n;++i)scanf("%d",&a[i].w),a[i].ind=0;
    for(int i=0;i<n;++i)scanf("%d",&a[i+n].w),a[i+n].ind=1;
    sort(a,a+n*2);
    for(int i=0;i<n*2;i++)
    {
        if(a[i].ind==0)ans+=a[i].w;
        i++;
        if(a[i].ind==1)ans-=a[i].w;
    }
    cout<<ans<<endl;
    return 0;
}

D. Slime
http://codeforces.com/contest/1038/problem/D

題幹:有n個史萊姆,他們可以互相吃對方,a吃了b以後會變成一個a-b(反過來吃就是b-a),可以是負數,問最後最大是多少。

一看,哇DP。然後再一看,哇greedy。

我們考慮一串數字a1,a2,…,an

對於一個數ai,當它被吃之後,就在它頭上填一個負號,然後和吃它的人綁定了去吃別人或者被別人吃。不管怎麼算,吃來吃去之後,對於一個數ai,它前面要麼是一個+,要麼是一個-。而本來的數值的絕對值是不會變的。

先考慮n=1,那沒什麼好說,就是它本身。

n=2,兩種情況,互相吃。

n>=3時,可以證明,其中任意一個數都可以隨便改變正負,但至少有一個數保持不變,最後結果等於他們的和。

先證明至少有一個保持符號不變。因為當一個沒吃過slime的slimeA“吃”掉一個slimeB,新產生的史萊姆中一定是A-B或者B-A。無論這個史萊姆吃什麼東西或者被什麼東西吃,裡面總是至少有一個改變過符號的和沒有改變過符號的。所以有一個符號不變得證。

然後是其他的可以隨便改變符號。這個問題其實就是,給你一串0,然後可以把任意一個0的左邊或者右邊的0變成1,1變成0。要得到一個給定的01串。這一定是做的到的。把這個順序反過來,就是本題吃的順序。

再處理一個特殊情況(全+或全-),就可以,以下給出程式碼

#include<iostream>
#include<cstdio>
using namespace std;

#define MIN(x,y) (((x)<(y))?(x):(y))
int n;
long long ans=0;
long long minn=1e10+7;
bool pos=0,neg=0;

long long read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    if(f==1)pos=1;
    if(f==-1)neg=1;
    return x;
}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        long long tmp;
        tmp=read();
        minn=MIN(minn,tmp);
        ans+=tmp;
    }
    if((pos==0||neg==0)&&n!=1)ans=ans-2*minn;
    if(n==1&&neg==1)ans=-ans;
    cout<<ans<<endl;
}

E. Maximum Matching
http://codeforces.com/contest/1038/problem/E

這題我自認為思路不錯,但是TLE了,希望大家幫幫我這個蒟蒻,嚶嚶嚶。

O(NlogN+K!)的複雜度,最壞情況(100^2+8!)*K(K為常數),為什麼會T呢,豹躁。

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

#define MIN(x,y) (((x)<(y))?(x):(y))
#define MAX(x,y) (((x)>(y))?(x):(y))
int n;
int al[5];
priority_queue<int> ed[5][5];
int sh[5][5];
int vis[5];
int ans=0;
int cur=0;

void dfs(int x)
{
    for(int i=1;i<=4;i++)
    {
        if(!ed[i][x].empty())
        {
            int edge=ed[i][x].top();
            cur+=edge;
            ed[i][x].pop();
            ed[x][i].pop();
            int needto=0;
            if(!vis[i])needto=1,cur+=al[i],vis[i]=1;
            int ttt=0;
            for(int j=1;j<=4;j++)if(!vis[j])ttt+=sh[i][j];
            cur+=ttt;
            dfs(i);
            cur-=ttt;
            if(needto)cur-=al[i],vis[i]=0;
            ed[i][x].push(edge);
            ed[x][i].push(edge);
            cur-=edge;
        }
    }
    ans=MAX(cur,ans);
    return;
}

int main()
{
    memset(al,0,sizeof(al));
    memset(sh,0,sizeof(sh));

    cin>>n;
    for(int i=0;i<n;i++)
    {
        int f,v,t;
        scanf("%d%d%d",&f,&v,&t);
        if(f==t)al[f]+=v;
        else
        {
            ed[f][t].push(v);
            ed[t][f].push(v); 
        }
    }

    for(int i=1;i<4;i++)
    for(int j=j+1;j<=4;j++)
    {
        while(ed[i][j].size()>2)
        {
            sh[i][j]+=ed[i][j].top();
            sh[j][i]+=ed[j][i].top();
            ed[i][j].pop();
            ed[j][i].pop();
            sh[i][j]+=ed[i][j].top();
            sh[j][i]+=ed[j][i].top();
            ed[i][j].pop();
            ed[j][i].pop();
        }
    }

    for(int i=1;i<=4;i++)
    {
        memset(vis,0,sizeof(vis));
        cur=0;
        vis[i]=1;
        cur+=al[i];
        for(int j=1;j<=4;j++)cur+=sh[i][j];
        dfs(i);
    }
    cout<<ans<<endl;
    return 0;
}