题目链接:http://codeforces.com/gym/101147
2017/8/27日训练赛,题目情况9/11,Rank 4/79。
A. The game of Osho
题意:定义一个子游戏,B,N可以从N减掉B^k要求B^k小于等于N,当N变成0,该哪个人选,哪个人就输了,给出G个这样的游戏,问最后的输赢情况。
解法:组合游戏,SG打表发现规律,然后求Nim和判断即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
//int cal(LL b, LL g){
// if(g==0) return 0;
// bool h[10];
// memset(h,0,sizeof(h));
// LL bb=1;
// while(bb<=g){
// int t=cal(b,g-bb);
// if(t<10) h[t]=1;
// if(b==1) break;
// bb*=b;
// }
// for(int x=0;x<10; x++)
// if(!h[x]) return x;
//}
int main()
{
// for(int x=1; x<=5; x++){
// for(int y=1; y<=5; y++){
// printf("%d %d %d\n", x,y,cal(x,y));
// }
// }
freopen("powers.in","r",stdin);
int T;
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
int ans = 0;
for(int i=0; i<n; i++){
int b,g;
scanf("%d%d",&b,&g);
if(b&1) ans^=g&1;
else{
if(g%(b+1)<=b-1) ans^=(g%(b+1))&1;
else ans^=2;
}
}
if(ans) puts("1");
else puts("2");
}
return 0;
}
B. Street
题意:给了一个平面,然后给了一些矩形有靠左边界的,也有靠着右边界的,现在问要从高度为0跑到高度问n+1,最少需要多少距离,你在矩形里面和边缘走是不需要时间的,题目给的是矩形的长宽,和离底面的距离和往哪靠。
解法:讨论两个矩形的距离,这里要分3种情况,然后建图跑最短路,Floyd T了,改成Dij AC。
队友代码:
C. The Wall
留坑。
D. Popcorn
题意:求n个人选k个人的方法数。
解法:就是C(n,k)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL C[22][22];
int main()
{
freopen("popcorn.in", "r", stdin);
memset(C, 0, sizeof(C));
for(int i=0; i<=20; i++){
C[i][0]=1;
for(int j=1; j<=i; j++){
C[i][j] = C[i-1][j-1]+C[i-1][j];
}
}
int T,n,m;
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n,&m);
printf("%lld\n", C[n][m]);
}
return 0;
}
E. Jumping
题意:一个人可以从1-n的任意位置出发,每个位置有一个d[i]表示这个人在这个位置可以向左跳d[i],也可以向右调d[i],问这个人从每个位置出发最后到达n的步数,不能到达输出-1。
解法:我们直接把n点看成起点,做一个最短路或者BFS,就可以求出N点到每个点的最短路了,不能到达就是dis[i]=INF的时候。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+2;
int dis[maxn];
bool f[maxn];
vector <int> G[maxn];
int main()
{
freopen("jumping.in","r",stdin);
int T,n;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i=1; i<=n; i++) G[i].clear();
for(int i=1; i<=n; i++){
int x;
scanf("%d", &x);
if(i-x>=1){
G[i-x].push_back(i);
}
if(i+x<=n){
G[i+x].push_back(i);
}
}
memset(dis, 0x3f, sizeof(dis));
memset(f, 0, sizeof(f));
dis[n] = 0;
queue <int> q;
q.push(n);
f[n] = 1;
while(q.size()){
int u = q.front();
q.pop();
for(int v : G[u]){
if(!f[v]){
dis[v] = dis[u]+1;
f[v] = 1;
q.push(v);
}
}
}
for(int i=1; i<=n; i++){
if(dis[i] > n) dis[i] = -1;
printf("%d\n", dis[i]);
}
}
return 0;
}
F. Bishops Alliance
题意:给了一个n*n的棋盘(n<=100000),然后棋盘上有m个皇后,问把这些皇后放到一个对角线上,当且仅当a[i]和a[j]的横坐标之差between i and j is at least equal to pi2 + pj2 + C描述能力捉急),a[i]和a[j]才可以放到一个对角线上。问最多可以放的象棋的个数。
解法:把一条对角线的棋子取出来之后,就是判断满足这个关系的棋子的个数,可以发现这个类似于LIS的DP,dp[i] = max(dp[j])+1, 其中j<=x - p[i]*p[i] - C +1,然后求出最大值后再更新到x+p[i]*p[i]的位置就可以 了。
复杂度:O(nlogn)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long LL;
struct node{
int x,y,p;
node(){}
node(int x,int y,int p):x(x),y(y),p(p){}
bool operator < (const node &rhs) const{
return x<rhs.x;
}
}a[maxn],b[maxn];
int C;
namespace BIT{
int c[maxn];
void init(){
memset(c, 0, sizeof(c));
}
inline int lowbit(LL x){
return x&-x;
}
void add(LL pos, int val){
for(LL i=pos; i<maxn; i+=lowbit(i))
c[i] = max(c[i], val);
}
void Clear(LL pos){
for(LL i=pos; i<maxn; i+=lowbit(i))
c[i] = 0;
}
int query(LL pos){
pos = min(pos, (LL)maxn-1);
int ret = 0;
for(LL i = pos; i>0; i-=lowbit(i))
ret = max(ret, c[i]);
return ret;
}
}
using namespace BIT;
vector <int> v1[maxn*2];
vector <int> v2[maxn*2];
int cal(vector <int> &v){
int n = v.size();
if(n == 0) return 0;
for(int i=0; i<n; i++) b[i] = a[v[i]];
sort(b, b+n);
int ret = 0;
for(int i=0; i<n; i++){
LL pos = b[i].x - (LL)b[i].p*b[i].p - C + 1;
int d = query(pos)+1;
ret = max(ret, d);
pos = b[i].x + (LL)b[i].p*b[i].p;
add(pos, d);
}
for(int i=0; i<n; i++){
LL pos = b[i].x + (LL)b[i].p*b[i].p;
Clear(pos);
}
return ret;
}
int main()
{
freopen("bishops.in","r",stdin);
int T; scanf("%d", &T);
while(T--){
int n,m;
scanf("%d %d %d", &n,&m,&C);
init();
for(int i=0; i<=n*2; i++){
v1[i].clear();
v2[i].clear();
}
for(int i=1; i<=m; i++){
scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].p);
v1[a[i].x+a[i].y].push_back(i);
v2[a[i].x-a[i].y+n].push_back(i);
}
int ans = 0;
for(int i=0; i<=n*2; i++){
ans = max(ans, cal(v1[i]));
ans = max(ans, cal(v2[i]));
}
printf("%d\n", ans);
}
return 0;
}
G. The Galactic Olympics
题意:抽象出来的题意:将p个物体划分成k个非空的可辨别的(可以理解为盒子有编号)集合的方法数。
解法:这个就是典型的第二类斯特林数。
第二类Stirling数 S(p,k)
S(p,k)的一个组合学解释是:将p个物体划分成k个非空的不可辨别的(可以理解为盒子没有编号)集合的方法数。 k!S(p,k)是把p个人分进k间有差别(如:被标有房号)的房间(无空房)的方法数。
S(p,k)的递推公式是:S(p,k)=k*S(p-1,k)+S(p-1,k-1) ,1<= k<=p-1 边界条件:S(p,p)=1 ,p>=0 S(p,0)=0 ,p>=1
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int dp[3005][3005];
const int mod = 1e9+7; int main()
{
freopen("galactic.in", "r", stdin);
dp[1][1] = 1;
for(int i=2; i<=3000; i++){
dp[i][i] = 1;
for(int j=1; j<=i; j++){
dp[i][j] = ((LL)j*dp[i-1][j]+dp[i-1][j-1])%mod;
}
}
int T;
scanf("%d", &T);
while(T--)
{
int n, k;
scanf("%d %d", &n,&k);
if(k > n){
puts("0");
}
else{
int ans = dp[n][k];
for(int i=1; i<=k; i++)
ans = (LL)ans*i%mod;
ans %= mod;
printf("%d\n", ans);
}
}
return 0;
}
H. Commandos
题意:给了一个3维的楼房,里面有些地方有人质,现在警察在第10层(1,1)的位置,给了这些警察可以行走的限制,问最多可以救出多少人质。
解法:就是3维的数塔问题,直接DP即可。
#include <bits/stdc++.h>
using namespace std;
int dp[12][12][12];
int val[12][12][12]; int main()
{
freopen("commandos.in", "r", stdin);
int T, n;
scanf("%d", &T);
while(T--){
memset(dp, 0, sizeof(dp));
memset(val, 0, sizeof(val));
scanf("%d", &n);
for(int i=0; i<n; i++){
int f,x,y,h;
scanf("%d %d %d %d", &f,&x,&y,&h);
val[f][x][y] += h;
}
for(int f=10; f>=1; f--){
for(int x=1; x<=10; x++){
for(int y=1; y<=10; y++){
dp[f][x][y] = max(dp[f][x][y], dp[f+1][x][y]);
dp[f][x][y] = max(dp[f][x][y], dp[f][x-1][y]);
dp[f][x][y] = max(dp[f][x][y], dp[f][x][y-1]);
dp[f][x][y] += val[f][x][y];
}
}
}
printf("%d\n", dp[1][10][10]);
}
return 0;
}
I. On the way to the park
题意:平面上给了一些圆的信息,然后给了一个半径R,就是你可以确定一个大圆R,这个大圆是在X轴上移动的,问这个大圆最多包含多少小圆,求这些小圆的半径和。
解法:我们把那些不可能被包含的小圆忽略,然后让剩下的小圆和大圆作相切,可以求出在什么范围内大圆可以包含这个小圆,然后把左端点打一个-r[i]的标记右端点打一个r[i]标记,排序之后求和并且更新最大值就是答案了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
typedef long long LL;
int n,m;
int x[maxn],y[maxn],r[maxn];
pair <double,int> a[maxn*3]; int main()
{
freopen("walk.in","r",stdin);
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n,&m);
int cnt = 0;
for(int i=1; i<=n; i++){
scanf("%d %d %d", &x[i],&y[i],&r[i]);
if(y[i]<0) y[i]=-y[i];
if(y[i]+r[i]>m) continue;
int d = m-r[i];
double dx=sqrt(1.0*d*d-1.0*y[i]*y[i]);
a[++cnt] = make_pair(x[i]-dx, -r[i]);
a[++cnt] = make_pair(x[i]+dx, r[i]);
}
LL sum=0,ans=0;
sort(a+1,a+cnt+1);
for(int i=1; i<=cnt; i++){
sum += -a[i].second;
ans = max(ans, sum);
}
printf("%lld\n", ans);
}
return 0;
}
J. Whistle's New Car
题意:给了n个点的图,图上每个点代表一个城市,并且拥有x[i]的值表示在这个城市可以加x[i]的油,定义城市i的吸引度为i有多少个子节点,只有在这个节点加油并且能跑到i点的节点个数,两个城市之间有边权代表路程。
解法:考虑到i点向上最远可以跑到哪个城市,在这个城市打上一个-1的标记,并且在当前点打上+1的标记,然后回溯的时候累加标记就可以 了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+10;
typedef long long LL;
vector <pair<int,int> > G[maxn];
int x[maxn];
int dep[maxn];
int stk[maxn];
int ans[maxn];
void dfs(int u, int fa, int d){
stk[d] = u;
int L = -1, R = d;
while(L < R){
int mid = (L+R+1)/2;
if(dep[stk[mid]]>=dep[u]-x[u]) R=mid-1;
else L=mid;
}
if(L>=0) --ans[stk[L]];
++ans[u];
for(auto &p : G[u]){
int v = p.first;
if(v == fa) continue;
dep[v] = dep[u] + p.second;
dfs(v, u, d+1);
ans[u] += ans[v];
}
} int main()
{
freopen("car.in", "r", stdin);
int T,n;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i=1; i<=n; i++){
G[i].clear();
scanf("%d", &x[i]);
ans[i]=0;
}
for(int i=1; i<n; i++){
int a,b,c;
scanf("%d%d%d", &a,&b,&c);
G[a].emplace_back(b,c);
G[b].emplace_back(a,c);
}
dfs(1,0,0);
for(int i=1; i<n; i++){
printf("%d ", ans[i]-1);
}
printf("%d\n", ans[n]-1);
}
return 0;
}
K. Touristic Trip
留坑。