题意:
给定n,k,要求构造一个1~n的排列a,
满足存在恰好k对:
gcd(a[i],i)=1.
如果无解输出-1.
数据范围:n<=1e6
思路:
这题具有一定迷惑性,我们看到肯定会想到质数和别的数的gcd是1,但是这样的话我们就相对于难一点去构造了,其实我们会发现,x和x+1的gcd一定是1,所以我们只需让x在x+1的位置上,然后头尾对调一下。以上操作对前k个数进行,保持后n-k个数不变,这样我们就能很简单的构造出来了。当然因为因为无论如何都有一个1的位置使得gcd==1,所以k=0的情况就不成立了。
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N=1e6;
int a[N];
int n,k;
signed main()
{
IOS;
cin>>n>>k;
if(k==0) cout<<"-1";
else
{
for(int i=1;i<=k;i++)
{
if(i!=k) cout<<(i+1);
else cout<<1;
cout<<" ";
}
for(int i=k+1;i<=n;i++)
{
cout<<i<<" ";
}
}
return 0;
}
题意:
红石头属于红队,蓝石头属于蓝队,分别给出所有红色蓝色石头在数轴上的位置,构造目标点的位置(实数),使得红队胜利且获得的分数尽可能多,红队的分数 等于 所有 比所有蓝石头离目标点近 的红石头 的数量,求 红队的最大分数或者如果无法赢就输出impossivle
思路:
说实话这题没转过来,参考题解后发现两个蓝色石头中间红色石头数量就是得分数(取max)
因为在两个蓝色石头之间的红色石头一定比所有蓝色石头更近c,且蓝色石头外的红色石头不满足比所有蓝色石头更近,因此,证为最优解
一道奇怪的贪心题剩下的我们直接二分找一下位置就好了,时间复杂度肯定能过的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int a[maxn],b[maxn];
int t,n,m;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
for(int i=1; i<=m; i++) scanf("%d",&b[i]);
sort(a+1,a+1+n);
sort(b+1,b+1+m);
//注意不要漏了0和最大值这两个边界
b[0]=0;
b[m+1]=1000000001;
int maxx=0;
for(int i=0; i<=m; i++){
int l=b[i], r=b[i+1];
//注意下标从1开始
int len=lower_bound(a+1,a+1+n,r)-1-upper_bound(a+1,a+1+n,l)+1;
maxx=max(maxx,len);
// printf("%d %d %d\n",x,y,maxx);
}
if(maxx==0) puts("Impossible");
else printf("%d\n",maxx);
}
return 0;
}
题意:
你每做一个烟花要n分钟,释放已做好的所有烟花需要m分钟,每只烟花成功释放的概率为p。问你在采取最优策略的前提下,直到成功释放第一个烟花时最小的期望时间花费。
思路:
我们可以设每制作k个烟花后集中释放一次。这个问题可以转化为每次制作并释放k个烟花,重复这一过程直到某次释放时成功出现一个烟花为止,求当前期望时间花费。这就变成了一个典型的几何分布问题。
每次开销kn+m,我们成功释放一个烟花的概率为(1-(1-p)^k),根据几何概型得到期望是1 / (1-(1-p)^k),然后我们就可以得到开销(kn+m) / (1-(1-p)^k)。
通过求二阶导后我们可以得到这是一个凹函数(过程太多,很难展示),求min,那我们就利用三分搞定他。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
ll n,m,p;
int t;
double px;
double fun(ll x){
return (n*x+m)*1.0/(1.0-pow(1.0-px,x));
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld",&n,&m,&p);
px=p*0.0001;
ll l=1,r=0x3f3f3f3f;
while(l < r - 1) //三分求凹函数极值
{
ll mid = (l + r) / 2;
ll mmid = (mid + r) / 2;
if(fun(mid) > fun(mmid)) l = mid;
else r = mmid;
}
int ans;
if(fun(l)<fun(r)) ans=l;
else ans=r;
// cout << ans << endl;
printf("%.10f\n",fun(ans));
}
return 0;
}
题意:
在一个图中,一个人从(0,0)进行上下左右行走。有一个地雷点(mx,my)。询问可不可以通过改变行走上下左右的顺序(不改变上下左右走的次数),可以避开雷。如果有多个,输出任意一种,如果没有,输出Impossible!
思路:
这题我一开始的分类情况可能是漏了,所以导致我WA了,而且hack样例也很难想,然后参考了一下题解。
其实我们要懂一个原则:
不管我们如何全排列给的字符串,我们最后都能走到终点,所以我们可以贪心UDLR每个都一次走完,然后我们全排列UDLR这个序列,在走的过程中判断是否会踩到地雷,最后我们要知道如果雷在起点和终点我们是必走不了的。
#include <bits/stdc++.h>
using namespace std;
int t,mx,my;
int stx,sty,edx,edy;
int a[4]={0,1,2,3};
int cnt[4];
int op[4][2]={0,1,0,-1,-1,0,1,0};//注意这里一定要一一对应
unordered_map<int,char> mp;
string s;
bool check(){//模拟全排列走一遍,边走边判断是否出现了踩到地雷的情况。
int x=0,y=0;
for(int i=0; i<4; i++){
for(int j=0; j<cnt[a[i]]; j++){
x+=op[a[i]][0];
y+=op[a[i]][1];
if(x==mx&&y==my) return false;
}
}
return true;
}
int main(){
scanf("%d",&t);
while(t--){
memset(cnt,0,sizeof cnt);
scanf("%d%d",&mx,&my);
cin >> s;
a[0]=0,a[1]=1,a[2]=2,a[3]=3;
mp[0]='U',mp[1]='D',mp[2]='L',mp[3]='R';
edx=0,edy=0,stx=0,sty=0;
for(int i=0; i<s.size(); i++){
if(s[i]=='U'){
edy++;
cnt[0]++;
}
else if(s[i]=='D'){
edy--;
cnt[1]++;
}
else if(s[i]=='L'){
edx--;
cnt[2]++;
}
else if(s[i]=='R'){
edx++;
cnt[3]++;
}
}
if((edx==mx&&edy==my)||(stx==mx&&sty==my)){
puts("Impossible");
continue;
}
int flag=0;
do{//全排列
if(check()){
//有一组可行解那就可以输出了
flag=1;
for(int i=0; i<4; i++){
for(int j=0; j<cnt[a[i]]; j++){
printf("%c",mp[a[i]]);
}
}
puts("");
break;
}
}while(next_permutation(a,a+4));
if(!flag) puts("Impossible");
}
return 0;
}
总结:这站的铜牌题考了个三分算法+概率论,其他都是思维题了,所以铜牌题的注重思维题,然后我们也得注重一下数论,其他算法的知识考得是比较浅的。