A 胖胖的牛牛
题解:用优先队列实现bfs,保存五个信息,当前坐标的x,y值;上一步的坐标值,pre_x,pre_y;从起点到这个点一共转了多少次方向val。那么如果当前的x,y与他前一步的前一步的x,y都不相等,代表转了一个方向,更新val,加入到优先队列中,如果执行完bfs还没有到终点输出-1即可
#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
const int maxn = 1e3+10;
char a[maxn][maxn];
struct node {
int prex,prey,x,y,c;
bool operator < (const node & t)const {
return c > t.c;
}
};
int xx[] = {-1,0,1,0};
int yy[] = {0,-1,0,1};
map<pi,int>mp;
int main()
{
int n,sx,sy,fx,fy;
cin >> n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin >> a[i][j];
if(a[i][j] == 'A')
{
sx = i,sy = j;
}
else if(a[i][j] == 'B')
{
fx = i,fy = j;
}
}
}
priority_queue<node>q;
q.push(node(sx,sy,sx,sy,0));
mp[mk(sx,sy)] = 1;
while(!q.empty())
{
node tmp = q.top();
q.pop();
int x = tmp.x;
int y = tmp.y;
int prex = tmp.prex;
int prey = tmp.prey;
int c = tmp.c;
if(x == fx && y == fy)
{
printf("%d\n",c);
return 0;
}
for(int i=0;i<4;i++)
{
int tx = x + xx[i];
int ty = y + yy[i];
if(a[tx][ty] == 'x' || mp[mk(tx,ty)] || tx < 1 || tx > n || ty < 1 || ty > n)continue;
mp[mk(x,y)] = 1;
int flag;
if(tx == prex || ty == prey)flag = 0;
else flag = 1;
q.push(node(x,y,tx,ty,c+flag));
}
}
puts("-1");
return 0;
}
B 牛牛的零食
题解:容斥定理,因为n只有15,我们可以用二进制来枚举状态,我们要求的是 l l l到 r r r区间中能被8整除,并且不能被数组中的任何一个数整除,我们先求出能被8整除的数有多少,然后减去能被8整除又能被数组中某些数整除的输的个数。那么如何求能被8整除又能被数组中某些数整除呢?我们求出用二机制表示的集合中的这些数的最小公倍数,与8再取一次lcm,拿区间右端点除以这个lcm。但是同一个区域可能减去多次,这时我们就要用到容斥了,单数就减,双数就加。
#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
ll a[25];
int n;
ll cal(int x)
{
ll ans = x/8;
for(int s=1;s<1<<n;s++)
{
int cnt = 0;;
ll sum = 8,flag = 0;
for(int i=0;i<n;i++)
{
if(s&(1<<i))
{
++cnt;
sum = sum/__gcd(sum , a[i+1])*a[i+1];
if(sum > x)
{
flag = 1;
break;
}
}
}
if(flag == 0)
{
if(cnt&1)ans -= x/sum;
else ans += x/sum;
}
}
return ans;
}
int main()
{
cin >> n;
for(int i=1;i<=n;i++)cin >> a[i];
int l,r;
cin >> l >> r;
cout<<cal(r)-cal(l-1)<<'\n';
return 0;
}
C 牛牛的最美味和最不美味的零食
题解:线段树,查询区间最大值最小值用最大值最小值线段树即可,那么如何实现删除一个数,然后查询呢?我们可以另外开一颗线段树,保存这个位置是否有值,那么查询第 l , r l,r l,r个数原来的位置就等于查询区间第 k k k个数,类似于权值线段树。
#include <bits/stdc++.h>
#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define ls o<<1
#define rs o<<1|1
#define mid (l+r)/2
const int maxn = 4e6+10,inf = 0x3f3f3f3f;
int tr[maxn],mi[maxn],ma[maxn];
void mer(int o)
{
tr[o] = tr[ls] + tr[rs];
mi[o] = min(mi[ls],mi[rs]);
ma[o] = max(ma[ls],ma[rs]);
}
int x;
void build(int o,int l,int r)
{
if(l == r)
{
scanf("%d",&x);
ma[o] = mi[o] = x;
tr[o] = 1;
return ;
}
build(lson);
build(rson);
mer(o);
}
void ups(int o,int l,int r,int p)
{
if(l == r)
{
tr[o] = 0;
mi[o] = inf;
ma[o] = -inf;
return ;
}
if(p <= mid)ups(lson,p);
else ups(rson,p);
mer(o);
}
int quId(int o,int l,int r,int k)
{
if(l == r)return l;
if(tr[ls] >= k)return quId(lson,k);
else return quId(rson,k-tr[ls]);
}
int quma(int o,int l,int r,int L,int R)
{
if(l >= L && r <= R)return ma[o];
int ans = -inf;
if(L <= mid)ans = max(ans,quma(lson,L,R));
if(R > mid)ans = max(ans,quma(rson,L,R));
return ans;
}
int qumi(int o,int l,int r,int L,int R)
{
if(l >= L && r <= R)return mi[o];
int ans = inf;
if(L <= mid)ans = min(ans,qumi(lson,L,R));
if(R > mid)ans = min(ans,qumi(rson,L,R));
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--)
{
int op,l,r;
scanf("%d%d",&op,&l);
if(op == 1)
{
l = quId(1,1,n,l);
ups(1,1,n,l);
}
else
{
scanf("%d",&r);
l = quId(1,1,n,l);
r = quId(1,1,n,r);
printf("%d %d\n",qumi(1,1,n,l,r),quma(1,1,n,l,r));
}
}
return 0;
}
D 瘦了的牛牛去旅游
题解: f l o y d floyd floyd的变形,我们知道弗洛伊德算法思想是 d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j]代表经过编号不超过k 的若干点作为中转点,i到j的最短路, d p [ k ] [ i ] [ j ] = m i n ( d p [ k ] [ i ] [ j ] , d p [ k − 1 ] [ i ] [ k ] + d p [ k − 1 ] [ k ] [ j ] ) dp[k][i][j] = min(dp[k][i][j] , dp[k-1][i][k] + dp[k-1][k][j]) dp[k][i][j]=min(dp[k][i][j],dp[k−1][i][k]+dp[k−1][k][j]),一般我们省掉第一维,那么我们可以添加一个维度, d p [ p ] [ k ] [ i ] [ j ] dp[p][k][i][j] dp[p][k][i][j]代表经过i到j经过p条边的最短路, d p [ p ] [ i ] [ j ] = m i n ( d p [ p ] [ i ] [ j ] , d p [ p − 1 ] [ i ] [ k ] + d p [ 1 ] [ k ] [ j ] ) dp[p][i][j] = min(dp[p][i][j] , dp[p-1][i][k] + dp[1][k][j]) dp[p][i][j]=min(dp[p][i][j],dp[p−1][i][k]+dp[1][k][j])
#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
const int maxn = 55,inf = 0x3f3f3f3f;
double ans[maxn][maxn];
int dp[maxn][maxn][maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(dp,0x3f,sizeof(dp));
for(int i = 1;i <= n;++i){
dp[i][i][0]=0;
}
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
dp[u][v][1] = min(dp[u][v][1],w);
}
for(int p=2;p<=n;p++)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) {
dp[i][j][p] = min(dp[i][j][p] , dp[i][k][p-1] + dp[k][j][1]);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i == j)continue;
ans[i][j] = inf;
for(int p=1;p<=n;p++)
if(dp[i][j][p]<inf){
ans[i][j] = min(ans[i][j] , 1.0*dp[i][j][p]/p);
}
}
int q;
scanf("%d",&q);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
if(inf != ans[l][r])printf("%.3lf\n",ans[l][r]);
else puts("OMG!");
}
return 0;
}
E 只能吃土豆的牛牛
题解:dfs,我们另原数组为:30,31,32…,我们可以简单的列出新数组前几项,1,3,1+3,9,1+9,3+9,1+3+9,27…,我们发现新生成的数组,每个在原数组中有的数比如3,9后面都跟着他在新数组中的下标减一个数,因为这个数可以跟他前面的每个数组成一个新的数。那么我们就可以递归写,dfs(n)代表第n个数是多少,具体看代码:
#include <bits/stdc++.h>
#define ll long long
#define pi pair<int,int>
#define mk make_pair
#define pb push_back
using namespace std;
ll dfs(int n)
{
if(n == 0)return 0;
if(n == 1)return 1;
if(n == 2)return 3;
ll sum = 1;
for(int i=1;;i++)
{
sum++;
sum += (sum - 1);
if(sum >= n)
{
sum = (sum + 1)/2;
return pow(3,i) + dfs(n-sum);
}
}
}
int main()
{
int T,kase = 0;
cin >> T;
while(T--)
{
int n;
cin >> n;
printf("Case #%d: %lld\n",++kase,dfs(n));
}
return 0;
}