Codeforces #284 Div 1 簡要題解

A. Crazy Town

題目鏈接

http://codeforces.com/contest/498/problem/A

題目大意

給你一個無限大的區域,這個區域被n 條形如Ax+By+C=0無限長的直線道路分割成若干個街區,給出A地和B地座標,問從A地到B地最少要穿過多少條道路,注意不能穿過直線與直線的交點

思路

很容易想到,最少的穿越次數,就是與線段AB不含端點的中間部分相交的直線個數,證明大家自己意識流一下就好了,其實就是看有多少直線把AB兩地完全分割開,這些直線是必須穿過的,而其他直線就不必穿過去了。

但是問題是怎麼找這些直線。我剛開始非常naive地去求每條直線和直線AB的交點,然後判斷交點是否在線段AB 上,各種特判不說,推導過程也很容易打錯。但是我看完一個紅名爺的代碼後才發現自己too simple了。注意到輸入的直線都是Ax+By+C=0 形式的,我們可以當成一個二元函數f(x,y) ,顯然只有f(xA,yA)f(xB,yB) 符號相反,才能讓這條直線穿過線段AB不含端點的中間部分

證明也很容易。假設這條直線和線段AB不含端點的中間部分的交點座標爲(xt,yt) ,顯然f(xt,yt)=0 ,而從(xt,yt) 開始,往一頭走的話,f(x,y) 的值會變爲負數,而往相反方向的另一頭走,f(x,y) 的值會變爲正數

紅名爺就是巨啊!

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXN 10000
#define EPS 1e-7

using namespace std;

typedef long long int LL;

LL a[MAXN],b[MAXN],c[MAXN];
int n;

int dcmp(LL x)
{
    if(x>0) return 1;
    if(x<0) return -1;
    return 0;
}

int main()
{
    LL x1,y1,x2,y2;
    scanf("%I64d%I64d%I64d%I64d",&x1,&y1,&x2,&y2);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%I64d%I64d%I64d",&a[i],&b[i],&c[i]);
    int tot=0;
    for(int i=1;i<=n;i++)
        if(dcmp(a[i]*x1+b[i]*y1+c[i])!=dcmp(a[i]*x2+b[i]*y2+c[i]))
            tot++;
    printf("%d\n",tot);
    return 0;
}

B. Name That Tune

題目鏈接

http://codeforces.com/contest/498/problem/B

題目大意

在《開門大吉》節目裏(大霧),主持人給了你n 首歌,每首歌對應一扇門,選手面對1n 號大門,依次按響門上的門鈴,門鈴會播放一段音樂(將一首經典流行歌曲以單音色旋律的方式演繹),對於第i 扇門,選手有Ti 秒的時間聽音樂,在每一秒時,選手有Pi% 的概率能聽出這首歌的名字,另外100Pi% 的概率需要等到下一秒繼續聽,但是在Ti 秒時選手一定能聽出這首歌的名字,求前T 秒選手可以聽出的歌曲個數的期望。

思路

可以想到用DP解決此題。用f[i][j] 表示當前到了第j 秒,正在聽第i 首歌並聽出這首歌的名字的概率。顯然f[0][0]=1 ,並可以推出下面的DP方程:

f[i][j]=f[i1][j1](1Pi)0Pi+f[i1][j2](1Pi)1Pi+...+f[i1][jTi](1Pi)Ti1Pi+f[i1][jTi](1Pi)Ti


f[i][j]=kTi1f[i1][jk](1Pi)k1Pi(k1k)+f[i1][jTi](1Pi)Ti(TiTi)

我們可以優化這個DP,由於f[i][j]f[i][j1] 的DP方程高度相似,因此我們可以維護f[i][j1] 裏的一個值tmp

tmp=f[i1][j2](1Pi)0+f[i1][j3](1Pi)1+...+f[i1][jTi1](1Pi)Ti1

那麼

f[i][j]=Pi(1Pi)tmp+f[i1][j1]Pi+f[i1][jTi](1Pi)Ti

這只是特殊情況,由於tmp 裏最多隻能有Ti 項,因此若jTi1>=0 ,則在DP到f[i][j] 時,需要在tmp 裏減去f[i1][jTi1](1Pi)Ti1

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXN 11000

using namespace std;

double f[2][MAXN],ans=0;
int n,t;
int now=1,pre=0;

int main()
{
    scanf("%d%d",&n,&t);
    f[pre][0]=1;
    for(int i=1;i<=n;i++)
    {
        memset(f[now],0,sizeof(f[now]));
        double Pi;
        int Ti;
        scanf("%lf%d",&Pi,&Ti);
        Pi/=100;
        double tmp=0;
        for(int j=i;j<=t;j++)
        {
            tmp*=(1-Pi);
            tmp+=f[pre][j-1];
            if(j-Ti-1>=0) tmp-=f[pre][j-Ti-1]*pow(1-Pi,Ti);
            f[now][j]+=tmp*Pi;
            if(j-Ti>=0) f[now][j]+=f[pre][j-Ti]*pow(1-Pi,Ti);
            ans+=f[now][j];
        }
        swap(now,pre);
    }
    printf("%.6f\n",ans);
    return 0;
}

C. Array and Operations

題目鏈接

http://codeforces.com/contest/498/problem/C

題目大意

給你n 個數a1...an ,以及m 個操作數對(i,j) ,每次操作時你可以選擇一個(i,j) ,統一除以ai,aj 的一個公約數,問你最多能進行多少次這樣的操作

思路

顯然,爲了讓步數最大化,我們每次除以的公約數都是質數。而且對於不同的除數操作,它們之間互不干擾,相同的除數操作會互相干擾。

我們可以先預處理篩素數,然後枚舉[1,109] 內的質數p ,用最大流求除以p 的操作最多能進行多少次。由於題面限制i+j 爲奇數,則顯然ij 中有一個是偶數,另一個是奇數,這樣就能構建出一個類似二分圖的網絡流模型:源點向所有下標爲奇數的點連容量爲這個點裏p 的出現次數,所有下標爲奇數的點向匯點連容量爲這個點裏p 的出現次數,對於每個操作(i,j)(i 爲奇數)ij 連容量爲這兩個點裏p 的出現次數的較小值,代表對(i,j) 做除p 操作的最大次數。最大流就是除以p 的操作最多能進行的次數。

然後每個ai 除完了以後可能還有剩下的一個大於109 的大質數。我們再一次建圖:源點向所有下標爲奇數的點連容量爲1或0(該點還剩餘的值大於1就是1,否則爲0),所有下標爲奇數的點向匯點連容量爲1或0(該點還剩餘的值大於1就是1,否則爲0),對於每個操作(i,j)(i 爲奇數),若i,j 剩餘的值均大於1,i 就向j 連容量爲1,再跑一次最大流加入答案即可。

代碼

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXV 1000
#define MAXE 21000
#define INF 0x3f3f3f3f

using namespace std;

typedef pair<int,int> pr;

int S,T;
int n,m;
int a[MAXV];
pr opt[MAXV];

struct edge
{
    int u,v,cap,next;
}edges[MAXE*2];

int head[MAXV],nCount=0;

void AddEdge(int U,int V,int C)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].cap=C;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

void add(int U,int V,int C)
{
    AddEdge(U,V,C);
    AddEdge(V,U,0);
}

int q[MAXE*2];
int layer[MAXV];
bool inX[MAXV],inY[MAXV];

bool CountLayer()
{
    memset(layer,-1,sizeof(layer));
    int h=0,t=1;
    q[h]=S;
    layer[S]=1;
    while(h<t)
    {
        int u=q[h++];
        for(int p=head[u];p!=-1;p=edges[p].next)
        {
            int v=edges[p].v;
            if(layer[v]==-1&&edges[p].cap)
            {
                layer[v]=layer[u]+1;
                q[t++]=v;
            }
        }
    }
    return layer[T]!=-1;
}

int DFS(int u,int flow)
{
    int used=0;
    if(u==T) return flow;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(layer[v]==layer[u]+1&&edges[p].cap)
        {
            int tmp=DFS(v,min(flow-used,edges[p].cap));
            edges[p].cap-=tmp;
            edges[p^1].cap+=tmp;
            used+=tmp;
            if(used==flow) break;
        }
    }
    if(!used) layer[u]=-1;
    return used;
}

int Dinic()
{
    int maxflow=0;
    while(CountLayer())
        maxflow+=DFS(S,INF);
    return maxflow;
}

bool isPrime[31623];
int primes[100000],tot=0;

void getPrime()
{
    memset(isPrime,true,sizeof(isPrime));
    isPrime[0]=isPrime[1]=false;
    for(int i=2;i<31623;i++)
    {
        if(!isPrime[i]) continue;
        primes[++tot]=i;
        for(int j=i+i;j<31623;j+=i)
            isPrime[j]=false;
    }
}

int cnt[MAXV]; //cnt[i]=a[i]可以除prime的次數

int solve(int prime)
{
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=n;i++)
        while(a[i]%prime==0)
        {
            a[i]/=prime;
            cnt[i]++;
        }
    nCount=1;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)
    {
        if(inX[i])
            add(S,i,cnt[i]);
        else
            add(i,T,cnt[i]);
    }
    for(int i=1;i<=m;i++)
        add(opt[i].first,opt[i].second,min(cnt[opt[i].first],cnt[opt[i].second]));
    return Dinic();
}

int main()
{
    int ans=0;
    getPrime();
    memset(head,-1,sizeof(head));
    S=MAXV-2,T=MAXV-1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(i&1) inX[i]=true;
        else inY[i]=true;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&opt[i].first,&opt[i].second);
        if(inX[opt[i].second])
            swap(opt[i].first,opt[i].second);
    }
    for(int i=1;i<=tot;i++)
        ans+=solve(primes[i]);
    memset(head,-1,sizeof(head));
    nCount=1;
    for(int i=1;i<=n;i++)
    {
        if(inX[i])
            add(S,i,1);
        else
            add(i,T,1);
    }
    for(int i=1;i<=m;i++)
        if(a[opt[i].first]==a[opt[i].second]&&a[opt[i].first]>1)
            add(opt[i].first,opt[i].second,1);
    ans+=Dinic();
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章