2021牛客暑期多校训练营1 (补题)
赛后补题,账号不足未参加
2021-07-17 12:00:00 至 2021-07-17 17:00:00
Problem A. Alice and Bob
题意 :Alice与Bob进行取石子游戏,一共有两堆,从一堆中去n个石子从另外一堆中取n*k个石子,不能取者输掉游戏,问谁获胜。
f[i][j]=1表示状态面临两堆石头数量为i,j时可以一步取光石头
f[i][j]=0表示状态面临两堆石头数量为i,j时无法一次取光石头
博弈必胜态是:一次操作能全部拿完
最后根据状态暴力求解SG数组,得到SG函数即可
代码如下:
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e3+5;
ll t,n,m;
bool f[N][N];
int main()
{
for(ri i=0;i<=5000;i++)
for(ri j=0;j<=5000;j++)
{
if(f[i][j]==0)
{
for(ri k=1;k+i<=5000;k++)
{
for(ri s=0;s*k+j<=5000;s++)
f[i+k][j+s*k]=1;
}
for(ri k=1;k+j<=5000;k++)
{
for(ri s=0;s*k+i<=5000;s++)
f[i+s*k][j+k]=1;
}
}
}
cin>>t;
while(t--)
{
cin >> n >> m;
string ans=f[n][m] ? "Alice" : "Bob";
cout << ans << '\n';
}
return 0;
}
Problem B. Ball Dropping
一道平面几何题目,可以用三角函数或者相似三角做(几何 × 贪心 √)
代码如下:
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=80112002;
const ll inf=999999999;
const ll N=5e4+5;
double r,a,b,h;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
scanf("%lf%lf%lf%lf",&r,&a,&b,&h);
if(2*r<=b) printf("Drop\n");
else
{
printf("Stuck\n");
double d=b*h/(a-b); //对应图中参数含义
double l=sqrt(d*d+b*b/4);
double ans=2*r*l/b-d;
printf("%.8lf\n",ans);
}
return 0;
}
Problem D. Determine the Photo Position
由于不允许分割、旋转或缩放图片,只能平移,而且老师只有一行,故每次考虑学生中每一行是否可以插入老师即可, 需要连续的背景'0'
统计每行连续'0'个数,进行插入并记录次数即可
代码如下:
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=80112002;
const ll inf=999999999;
const ll N=5e4+5;
ll n,m;
string a[3000],b;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> n >> m;
for(ri i=1;i<=n;i++) cin >> a[i];
cin >> b;
ll ans=0;
ll cnt=0;
for(ri i=1;i<=n;i++)
{
for(ri j=0;j<n;j++)
{
if(a[i][j]=='0') ++cnt;
if(a[i][j]!='0'||j==n-1)
{
if(cnt>=m) ans+=(cnt-m+1);
cnt=0;
}
}
}
cout << ans << '\n';
return 0;
}
Problem E. Escape along Water Pipe
题意 :有一张图,有入口出口,全部布满6中开口方向不同的水管,可以适当旋转水管0,90,180,270度,问能否从入口走到出口
dfs遍历图以及周围水管旋转后的情况,能到出口就yes,并且旋转次数k,输出旋转度数和坐标,不能就no
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e3+5;
ll dx[4]={1,0,-1,0};
ll dy[4]={0,-1,0,1};
ll t,n,m;
ll w[1050][1050];
ll type[4][4]=
{
{5,0,-1,1},
{2,4,1,-1},
{-1,3,5,2},
{3,-1,0,4},
};
struct node
{
ll x,y,dir;
}pre[1050][1050][4];
bool vis[1050][1050][4];
bool check(int x,int y,int dir)
{
if(x==n+1&&y==m&&dir==0) return true;
if(vis[x][y][dir]) return false;
if(x<1||y<1||x>n||y>m) return false;
return true;
}
bool bfs()
{
queue<node> q;
q.push(node{1,1,0});
vis[1][1][0]=true;
ll x,y,dir;
while(!q.empty())
{
node tmp=q.front(); q.pop();
x=tmp.x,y=tmp.y,dir=tmp.dir;
ll ndir,nx,ny;
if(w[x][y]<4)
{
ndir=(dir+1)%4,nx=x+dx[ndir],ny=y+dy[ndir];
if(check(nx,ny,ndir))
{
q.push(node{nx,ny,ndir});
vis[nx][ny][ndir]=true;
pre[nx][ny][ndir]=tmp;
}
if(nx==n+1&&ny==m&&ndir==0) return true;
ndir=(dir+3)%4; nx=x+dx[ndir]; ny=y+dy[ndir];
if(check(nx,ny,ndir))
{
q.push(node{nx,ny,ndir});
vis[nx][ny][ndir]=true;
pre[nx][ny][ndir]=tmp;
}
}
else
{
ndir=dir,nx=x+dx[ndir],ny=y+dy[ndir];
if(check(nx,ny,ndir))
{
q.push(node{nx,ny,ndir});
vis[nx][ny][ndir]=true;
pre[nx][ny][ndir]=tmp;
}
if(nx==n+1&&ny==m&&ndir==0) return true;
}
}
return false;
}
void dfs(int x,int y,int dir,int tp)
{
if(x==1&&y==1&&dir==0)
{
printf("%d\n",tp*2);
return ;
}
node tmp=pre[x][y][dir];
dfs(tmp.x,tmp.y,tmp.dir,tp+1);
ll TYPE=type[tmp.dir][dir];
printf("1 %d %d %d\n",(TYPE-w[tmp.x][tmp.y]+4)%4*90,tmp.x,tmp.y);
w[tmp.x][tmp.y]=TYPE;
printf("0 %d %d\n",tmp.x,tmp.y);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) scanf("%d",&w[i][j]);
if(!bfs()) printf("NO\n");
else
{
printf("YES");
dfs(n+1,m,0,0);
}
}
return 0;
}
Problem F. Find 3-friendly Integers
题意 :一个正整数是 3-friendly 当且仅当我们可以找到一个连续的 子串 的十进制表示,并且该子串所表示的十进制整数是 3 的倍数。
给出l,r求[l,r]中3-friendly个数。
思维题,cf有类似的题目,脑子不够用啊,想不到
可以证明,三位数abc,必定是符合要求3-friendly Integers
比如长度为3的一个数a b c,假如a % 3 = 0则a就是3的倍数。
假如a % 3 = 1 ,那么b % 3 不能等于2,也不能等于0。等于0则 b 就直接是3的倍数,等于2则( a + b ) % 3 = 0 ,ab 就是3的倍数。因此b % 3 必 须 是 1 ,因此c % 3无论是0 , 1 还是2,都能找到3的倍数。
假如a % 3 = 2 ,同样能证明。
因此只要统计1-99之间得个数就可以
直接做check函数拆开判断即可,打表用a[]保存 a[i]表示1-i中符合要求得个数
代码如下:
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=80112002;
const ll inf=999999999;
const ll N=5e4+5;
ll l,r,t;
ll a[105];
bool check(ll x)
{
if(x<=9) return x%3==0;
else
{
ll a,b,k;
k=x;
a=k%10,k/=10,b=k;
return (a%3==0||b%3==0||(a+b)%3==0);
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
for(ri i=2;i<=99;i++)
{
if(check(i)) a[i]=a[i-1]+1;
else a[i]=a[i-1];
}
cin >> t;
while(t--)
{
ll ans=0;
cin >> l >> r;
if(l<=99)
{
if(check(l)) ans=-(a[l]-1);
else ans=-a[l];
if(r<=99) ans+=a[r];
else ans+=(r-100+1+a[99]);
}
else ans=r-l+1;
cout << ans << '\n';
}
return 0;
}
Problem G. Game of Swapping Numbers
题意 :对于数组大小为 n 的两个数组 a,b,在a上进行 k 次交换,使得 ai-bi绝对值和和最大
贪心思想,去绝对值讨论,a>b时,做差为0,a<b时,做差2b-2a=2min(ai,bi)-2max(aj,bj),故更新符合这个条件即可
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e5+5;
ll n,m,k;
ll a[N],b[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin >> n >> k;
for(ri i=1;i<=n;i++) cin >> a[i];
for(ri i=1;i<=n;i++) cin >> b[i];
ll ans=0;
for(ri i=1;i<=n;i++)
{
if(a[i]>b[i]) swap(a[i],b[i]);
ans+=b[i]-a[i];
}
sort(a+1,a+1+n);
sort(b+1,b+1+n);
ll x=0;
while(x<k&&x<n&&a[n-x]>b[x+1])
{
ans+=2*(a[n-x]-b[x+1]);
x++;
}
cout << ans << '\n';
return 0;
}
Problem H. Hash Function
题意 :给定一个有n项的数列a,选定一个最小的seed使得用其构造的hash不会冲突
根据同余定理可知,若A-B=C且模数为C的约数,则必然会是A%p=B%p。所以只需要统计出N个数的两两差值即可,可以用FFT/NTT优化(不太明白插个眼)
卡数据可以暴力枚举
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e5+5;
ll a[N];
ll vis[N];
ll n;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(ri i=1;i<=n;i++) cin>>a[i],vis[a[i]]=1;
sort(a+1,a+n+1);
for(ri ans=n;;ans++)
{
ll f=0;
for(ri j=n;a[j]>=ans;j--)
{
if(vis[a[j]%ans])
{
f=1;
break;
}
}
if(!f)
{
cout << ans << '\n';
break;
}
}
return 0;
}
Problem K. Knowledge Test about Match
题意 :给出长度为n的a,b数组,任意调整数组b中的元素顺序,使得它们的loss函数最小
loss函数:
数学题目loss函数越接近1该函数值越大,故优先选择相等的数,用贪心的思想将出现次数多的放在前面进行求解。
#include <bits/stdc++.h>
#define ri int
typedef int lll;
typedef long long ll;
using namespace std;
const ll mod=1000000007;
const ll inf=999999999;
const ll N=5e5+5;
ll n,t;
ll a[N],b[N];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--)
{
memset(a,-1,sizeof(a));
memset(b,0,sizeof(b));
cin >> n;ll x;
for(ri i=0;i<n;i++)
{
cin >> x;
b[x]++;
}
for(ri i=0;i<n;i++)
for(ri j=0;j<n;j++)
if(b[j]!=0)
{
if(b[j]>0 && j+i<n && a[j+i]==-1)
{
a[j+i]=j;
b[j]--;
}
if(b[j]>0 && j-i>=0 && a[j-i]==-1)
{
a[j-i]=j;
b[j]--;
}
}
for(ri i=0;i<n;i++)
cout << a[i] << ' ';
cout << '\n';
}
}