Farmer John 的奶牛们最近成为了一个简单的数字游戏“FizzBuzz”的狂热玩家。这个游戏的规则很简单:奶牛们站成一圈,依次从一开始报数,每头奶牛在轮到她的时候报一个数。如果一头奶牛将要报的数字是 3 的倍数,她应当报“Fizz”来代替这个数。如果一头奶牛将要报的数字是 5 的倍数,她应当报“Buzz”来代替这个数。如果一头奶牛将要报的数字是 15 的倍数,她应当报“FizzBuzz”来代替这个数。于是这个游戏的开始部分的记录为:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16
由于词汇的匮乏,奶牛们玩的 FizzBuzz 中用“Moo”代替了 Fizz、Buzz、FizzBuzz。于是奶牛版的游戏的开始部分的记录为:
1, 2, Moo, 4, Moo, Moo, 7, 8, Moo, Moo, 11, Moo, 13, 14, Moo, 16
给定 N(1≤N≤1e9),请求出这个游戏中第 N 个被报的数。
利用容斥原理+二分判断。我们假设 n 个数里共有 cnt 个被跳过的数,那么报的第 n 个数就是 n+cnt ,由于 n 和 cnt 都具有单调性,所以我们可以利用二分判定来解决。
其中容斥原理很容易想到,cnt 显然等于 n 内 3 的倍数 + 5 的倍数 +15的倍数,但是15 = 3 * 5,所以15不需要重复统计(已经在 3 的倍数中统计过了)。但是我们重复统计了 3 和 5 的公倍数(统计了 2 次),于是我们就要减去同时是 3 的倍数和 5 的倍数 的 个数即可得到cnt。
在O(1)时间内求出cnt后,我们就可以在O(logN)时间内用二分搜索解决此题。
#include
using namespace std;
typedef long long ll;
const ll INF = 1e18;
int tn;
int check(ll n){
ll a = n/3, b = n/5;
ll c = n/15;
ll cnt = a + b - c;
return n-cnt >= tn;
}
int main(){
freopen("moobuzz.in","r",stdin);
freopen("moobuzz.out","w",stdout);
cin >> tn ;
ll l = 1,r = INF;
while(l <= r){
ll mid = l+r>>1;
if(check(mid)) r = mid-1;
else l = mid+1;
}
cout << l << endl;
return 0;
}
有两个牛棚位于一维数轴上的点 0 和 L 处(1≤L≤1e9)。同时有 N 头奶牛(1≤N≤5e4)位于数轴上不同的位置(将牛棚和奶牛看作点)。每头奶牛 i 初始时位于某个位置 xi,并朝着正向或负向以一个单位每秒的速度移动,用一个等于 1 或 −1 的整数 di 表示。每头奶牛还拥有一个在范围 [1,1e3] 内的重量。所有奶牛始终以恒定的速度移动,直到以下事件之一发生:
如果奶牛 i 移动到了一个牛棚,则奶牛 i 停止移动。
当奶牛 i 和 j 占据了相同的点的时候,并且这一点不是一个牛棚,则发生了相遇。此时,奶牛 i 被赋予奶牛 j 先前的速度,反之亦然。注意奶牛可能在一个非整数点相遇。
令 T 等于停止移动的奶牛(由于到达两个牛棚之一)的重量之和至少等于所有奶牛的重量之和的一半的最早时刻。请求出在时刻 0…T(包括时刻T)之间发生的奶牛对相遇的总数。
这道题与 Ants 那题类似。如果不考虑重量,那么我们可以忽略每头牛的个性,从整体来看,如果两头牛相遇可以看作他们“穿过”对方并继续前进;如果每头牛有不同的重量,那么它们就有了个性,不能再等价于“穿过”对方。
这个时候我们可以从两个角度来考虑:
通过第一个推论我们可以求出 T (二分或者优先队列),通过第二个推论我们可以根据 T 来求出相遇的牛的总数。
#include
using namespace std;
const int N = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int n,l;
ll sum;
int w[N],px[N],d[N];
bool check(int t){
//t时刻到家的重量是否达到一半
int tw = 0, c1 = 0, c2 = 0;
for(int i = 1;i <= n;i++){
if(d[i] == 1 && l - px[i] <= t) c1++;
if(d[i] == -1 && px[i] <= t) c2++;
}
for(int i = 1;i <= c2;i++) tw += w[i];
for(int i = n;i > n-c1;i--) tw += w[i];
//printf("%d %d %d %d %d\n",t,c1,c2,tw,sum);
return tw*2 < sum;
}
ll a[N];
int tot = 0;
int bsearch(ll x){
int l = 1,r = tot;
while(l <= r){
int mid = l+r>>1;
if(a[mid] >= x) r = mid-1;
else l = mid+1;
}
//cout << x << " " << l << endl;
return l;
}
int calc(ll t){
int res = 0; //相遇次数
tot = 0;
for(int i = 1;i <= n;i++)
if(d[i] == -1) a[++tot] = px[i];
for(int i = 1;i <= n;i++)
if(d[i] == 1) res += bsearch(px[i]+2*t+1) - bsearch(px[i]);
return res;
}
struct Node{
int w,x,d;
bool operator < (const Node& rhs) const{
return x < rhs.x;
}
}nodes[N];
int main(){
freopen("meetings.in","r",stdin);
freopen("meetings.out","w",stdout);
scanf("%d%d",&n,&l);
for(int i = 1;i <= n;i++)
scanf("%d%d%d",&nodes[i].w,&nodes[i].x,&nodes[i].d);
sort(nodes+1,nodes+1+n);
for(int i = 1;i <= n;i++) w[i] = nodes[i].w,px[i] = nodes[i].x,d[i] = nodes[i].d;
for(int i = 1;i <= n;i++) sum += w[i];
int tl = 0,tr = l;
while(tl <= tr){
int mid = tl+tr>>1;
if(check(mid)) tl = mid+1;
else tr = mid-1;
}
//cout << l << endl;
printf("%d\n",calc(tl)); //计算 l 秒内碰头次数
return 0;
}
Farmer John 计划建造 N(1≤N≤1e5)个农场,用 N−1 条道路连接,构成一棵树(也就是说,所有农场之间都互相可以到达,并且没有环)。每个农场有一头奶牛,品种为更赛牛或荷斯坦牛之一。
Farmer John 的 M 个朋友(1≤M≤1e5)经常前来拜访他。在朋友 i 拜访之时,Farmer John 会与他的朋友沿着从农场 Ai 到农场 Bi 之间的唯一路径行走(可能有 Ai=Bi)。除此之外,他们还可以品尝他们经过的路径上任意一头奶牛的牛奶。由于 Farmer John 的朋友们大多数也是农场主,他们对牛奶有着极强的偏好。他的有些朋友只喝更赛牛的牛奶,其余的只喝荷斯坦牛的牛奶。任何 Farmer John 的朋友只有在他们访问时能喝到他们偏好的牛奶才会高兴。
请求出每个朋友在拜访过后是否会高兴。
一条道路只有 3 种情况,全是 H 牛,全是 G 牛或者两者兼有。那么如果节点 x 是 H 牛我们就令其w = -1,如果是 G 牛就等于1。那么这一段路径上所有点的 w 相加,如果等于 d,就说明全是G牛;如果等于 -d,就说明全是 H 牛;否则就说明两者都有。
至于快速求两点间距离可以用倍增求LCA。
#include
using namespace std;
const int N = 1e5+10;
const int M = 2*N;
char ty[N];
int n,m,w[N];
int head[N],ver[M],edge[M],nex[M],tot = 1;
void addEdge(int x,int y,int z){
ver[++tot] = y; edge[tot] = z;
nex[tot] = head[x]; head[x] = tot;
}
int dis[N],anc[N][25],dep[N],sum[N];
void dfs(int x){
for(int i = 1;i <= 22;i++)
anc[x][i] = anc[anc[x][i-1]][i-1];
for(int i = head[x];i ;i = nex[i]){
int y = ver[i], z = edge[i];
if(dis[y] || y == 1) continue;
dep[y] = dep[x] + 1;
dis[y] = dis[x] + z;
anc[y][0] = x;
sum[y] = sum[x] + w[y];
dfs(y);
}
}
int lca(int x,int y){
if(dep[x] < dep[y]) swap(x,y);
for(int i = 22;i >= 0;i--)
if(dep[anc[x][i]] >= dep[y]) x = anc[x][i];
if(x == y) return x;
for(int i = 22;i >= 0;i--){
if(anc[x][i] != anc[y][i]){
x = anc[x][i]; y = anc[y][i];
}
}
return anc[x][0];
}
int main(){
freopen("milkvisits.in","r",stdin);
freopen("milkvisits.out","w",stdout);
scanf("%d%d",&n,&m);
scanf("%s",ty+1);
for(int i = 1;i <= n;i++)
if(ty[i] == 'G') w[i] = 1;
else w[i] = -1;
for(int i = 1,x,y;i < n;i++){
scanf("%d%d",&x,&y);
addEdge(x,y,1); addEdge(y,x,1);
}
char s[10];
dep[0] = -1; dfs(1);
for(int i = 1,x,y;i <= m;i++){
scanf("%d%d%s",&x,&y,s);
int fa = lca(x,y);
int d = dis[x]+dis[y] - 2*dis[fa] + 1;
int sw = sum[x]+sum[y] - 2*sum[fa]+w[fa];
if(s[0] == 'H' && sw != d) putchar('1');
else if(s[0] == 'G' && sw != -d) putchar('1');
else putchar('0');
}
return 0;
}