A. Crazy Town
题目链接
http://codeforces.com/contest/498/problem/A
题目大意
给你一个无限大的区域,这个区域被
n
条形如
思路
很容易想到,最少的穿越次数,就是与线段 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
首歌,每首歌对应一扇门,选手面对
思路
可以想到用DP解决此题。用
即
我们可以优化这个DP,由于
f[i][j]
与
f[i][j−1]
的DP方程高度相似,因此我们可以维护
f[i][j−1]
里的一个值
tmp
那么
这只是特殊情况,由于 tmp 里最多只能有 Ti 项,因此若 j−Ti−1>=0 ,则在DP到 f[i][j] 时,需要在 tmp 里减去 f[i−1][j−Ti−1](1−Pi)Ti−1
代码
#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
个数
思路
显然,为了让步数最大化,我们每次除以的公约数都是质数。而且对于不同的除数操作,它们之间互不干扰,相同的除数操作会互相干扰。
我们可以先预处理筛素数,然后枚举
[1,109−−−√]
内的质数
p
,用最大流求除以
然后每个
ai
除完了以后可能还有剩下的一个大于
109−−−√
的大质数。我们再一次建图:源点向所有下标为奇数的点连容量为1或0(该点还剩余的值大于1就是1,否则为0),所有下标为奇数的点向汇点连容量为1或0(该点还剩余的值大于1就是1,否则为0),对于每个操作
(i,j)
(
i
为奇数),若
代码
#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;
}