大一寒假集训-线上集训第一次考试
Problem A:NEFU1295 机器人
对数字进行倒置,用结构体存所得数和原数。
注意:如果再将所得数倒置再输出则需考虑个位是否为0,较麻烦。
#include<bits/stdc++.h>
using namespace std;
//typedef long long int;
struct node{
int ii, tmp;
};
priority_queue<node, vector<node> > q;
int rev(int num)
{
int t=0;
while(num)
{
t = t*10+num%10;
num /= 10;
}
return t;
}
bool operator < (const node &s1, const node &s2)
{
return s1.ii>s2.ii;
}
int main()
{
//ios::sync_with_stdio(false);
int n;
int i;
while(~scanf("%d", &n))
{
while(n--)
{
scanf("%d", &i);
q.push({rev(i), i});
}
while(q.size()>1)
{
printf("%d ", q.top().tmp);
q.pop();
}
printf("%d\n", q.top().tmp);
q.pop();
}
return 0;
}
Problem B:NEFU1311 纸牌游戏
算是道队列的模板题。
#include<bits/stdc++.h>
using namespace std;
//queue<int> q;
int main()
{
ios::sync_with_stdio(false);
int t, n, a[55], tot;
cin>>t;
while(t--)
{
queue<int>q;
cin>>n;
if(n==1)
{
cout<<1<<endl;
continue;
}
tot = 0;
fill(a, a+55, 0);
for(int i=1; i<=n; i++)
q.push(i);
while(q.size() > 1)
{
a[++tot] = q.front();
q.pop();
if(q.size() > 1)
{
int tmp = q.front();
q.pop();
q.push(tmp);
}
}
for(int i=1; i<tot; i++)
cout<<a[i]<<",";
cout<<a[tot]<<endl<<q.front()<<endl;
}
return 0;
}
Problem C:NEFU2114 咸鱼连突刺
区间问题,二分+素数筛。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int cnt=0, prime[maxn];
bool flag[maxn];
long long ans=0;
void init()
{
fill(flag, flag+maxn, 1);
flag[0]=0;
flag[1]=0;
for(int i=2; i<=maxn-1; i++)
{
if(flag[i])
{
prime[++cnt] = i;
}
for(int j=1; j<=cnt&&prime[j]*i<=maxn-1; j++)
{
flag[prime[j]*i] = 0;
if(!(i%prime[j])) break;
}
}
}
void answer(int L, int R)
{
//ans = 0;
int l = lower_bound(prime+1, prime+cnt+1, L)-prime;
int r = upper_bound(prime+1, prime+cnt+1, R)-prime;
if(l==cnt+1 || r==cnt+1) return;
r--;
if(prime[l]>R || prime[r]<L) return;
ans += prime[l]+prime[r];
}
int main()
{
ios::sync_with_stdio(false);
init();
int t;
cin>>t;
while(t--)
{
int l, r;
cin>>l>>r;
answer(l, r);
}
cout<<ans<<endl;
return 0;
}
Problem D:NEFU2110 库特的合并果子
优先队列模板题。
此题使用ios::sync_with_stdio(false);
会RE。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue<ll, vector<ll>, greater<ll> > q;
int main()
{
//ios::sync_with_stdio(false);
int n, i;
while(cin>>n)
{
while(n--)
{
cin>>i;
q.push(i);
}
while(q.size() > 2)
{
ll t1=q.top();
q.pop();
ll t2=q.top();
q.pop();
q.push(t1+t2);
cout<<t1+t2<<" ";
}
ll t1=q.top();
q.pop();
ll t2=q.top();
q.pop();
//q.push(t1+t2);
cout<<t1+t2<<endl;
//q.pop();
}
return 0;
}
代码没什么好说的地方,但是优先队列的nlogn的常数还是蛮大的,其实从时间角度并不是很优的,这道题目是有sort完后o(n)的做法的,也有o(a[i]范围)做法的,都比这个方法优秀,有兴趣的同学可以自己去看一下洛谷P6033。
Problem E:NEFU2111 库特的素数队列(1)
素数+队列按照题意模拟,反复迭代即可,当然队列部分也可用数组链表等等。
开两个队列互相传送:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7+1;
int cnt=0, max1=1e7, prime[maxn];
bool flag[maxn];
void init()
{
fill(flag, flag+maxn, 1);
flag[0]=0;
flag[1]=0;
for(int i=2; i<=max1; i++)
{
if(flag[i])
{
prime[++cnt] = i;
}
for(int j=1; j<=cnt&&prime[j]*i<=max1; j++)
{
flag[prime[j]*i] = 0;
if(!(i%prime[j])) break;
}
}
}
int main()
{
ios::sync_with_stdio(false);
int t, n, cnt, tot;
init();
cin>>t;
while(t--)
{
tot=0;
queue<int> q;
queue<int> p;
cin>>n;
for(int i=1; i<=n; i++)
q.push(i);
while(q.size()+p.size() > 1)
{
++tot;
int tmp = q.front();
if(flag[tot])
p.push(tmp);
q.pop();
if(q.empty())
{
tot = 0;
//swap(p, q);
q.swap(p);
//p.swap(q);
}
}
if(!q.empty())
{
cout<<q.front()<<endl;
q.pop();
}
else
{
cout<<p.front()<<endl;
p.pop();
}
}
return 0;
}
这里给出马老三异或版本:
int main()
{
//ios::sync_with_stdio(false);
init();
int t;
scanf("%d", &t);
while (t--)
{
int n;
scanf("%d", &n);
queue<int> q[2];
int cnt = 0;
for (int i = 1; i <= n; i++)
q[0].push(i);
int fx = 0;
while (q[1].size() + q[0].size() > 1)
{
++cnt;
int t = q[fx].front();
q[fx].pop();
if (flag[cnt] == 0)
q[fx ^ 1].push(t);
if (q[fx].empty())
{
cnt = 0;
fx ^= 1;
}
}
if (!q[1].empty())
{
cout << q[1].front() << endl;
q[1].pop();
}
else
{
cout << q[0].front() << endl;
q[0].pop();
}
}
return 0;
}
Problem F:NEFU2112 库特的素数队列(2)
费大时间打了个表,结果搞起了二分
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
int yu[13]={0, 1, 2, 3, 5, 11, 31, 127, 709, 5381, 52711, 648391, 9737333};
int main()
{
ios::sync_with_stdio(false);
//freopen("a.txt", "w", stdout);
int t, n, cnt, tot;
cin>>t;
while(t--)
{
cin>>n;
cout<<yu[upper_bound(yu+1, yu+13, n)-yu-1]<<endl;
}
return 0;
}
在线
- 首先我们可以得出一个结论,显然在轮数相同情况下最后剩下的人的序号是确定的。随着n增加,只有让轮数增加才能使最后答案变化,那什么时候轮数会增加呢?我们不难发现只有当多了一个人并且他站到了之前的最后一轮,使最后一轮的人数从一个变成两个才能增加轮数,那怎么确定每增加一个人他是否能站到最后一轮呢?我们递推记录一下每个点的答案,如果n的因子数对应的答案和n-1对应的答案相同了,可以看出n变到n的因子需要一轮,所以可以看出此时n比n-1多一轮了,而n又是在最后一轮两个数的时候处于第二个数的位置,所以n点对应输出的答案就是n。
- 我们通过上面(1)这个版本也很容易看出最后可能成为输出答案的数并不多,那么我们每次二分找每次答案的第一次变化时的值迭代递推过去就行,但是这种方法其实是带2个log的(一个是二分的log,一个是迭代次数的log,两个log相乘关系),但是对于t=3e4也可以轻松过的
离线
既然我们知道答案不多而且每个数x对应的答案都是答案组里小于等于x的最大值,我们只要找到答案组就ok了。提供三种找答案组的方法
- 手动找 我们输入上限1e7可以得到一个答案x,然后我们输入x-1得到答案y,重复11次就可以找到所有答案组。
- 二分 手动二分或者用程序二分找答案第一次变化的位置即可,也是反复找11次即可
- 找规律,也可以把前100个数打个表,我们就可以发现答案组有如下规律:
答案组第一个成员是1,然后下一个成员是第一个素数2,然后下一个成员是第2个素数3,然后下一个成员是第3个素数5,然后下一个成员是第5个素数11,然后下一个成员是第11个素数31…找到了这个规律直接把答案组直接递归输出出来即可。
Problem G:NEFU1949 库特的绳子
把钉子看作点,那么我们用结构体来存一条线。
struct node{
int L, R, dis;
};
考虑到绳子长度和钉子间距离的问题,自然想到优先队列可以自动排序的优势。
priority_queue<node, vector<node> > que;
随后我们分别读入钉子和绳子,并将钉子距离(线)存到结构体中。
cin>>point[1];
For(i, 2, n)
{
cin>>point[i];
que.push({i-1, i, point[i]-point[i-1]});
}
For(i, 1, m) cin>>rope[i];
接下来将所有的线处理出来。
For(i, 1, m)
{
if(que.empty())
{
flag = false;
break;
}
node tmp = que.top();
want[i] = tmp.dis;
if(tmp.R+1 <= n)
{
tmp.dis += (point[tmp.R+1]-point[tmp.R]);
tmp.R += 1;
que.push(tmp);
}
que.pop();
}
将绳子数组排序后就可以在钉子上挂绳子了。
由于钉子距离和绳子长度均已排好序且任意两个钉子间只能挂有一根绳子,所以直接逐一比对即可。
For(i, 1, m)
if(rope[i] < want[i])
{
flag = false;
break;
}
附上完整代码:
#include<bits/stdc++.h>
#define For(i, l, r) for(int i=l; i<=r; i++)
using namespace std;
struct node{
int L, R, dis;
};
bool operator < (const node &p, const node &q) // 重载运算符
{
return p.dis > q.dis;
}
const int maxn = 5e5+20;
int point[maxn], rope[maxn], want[maxn];
int main()
{
ios::sync_with_stdio(false);
int n, m;
while(cin>>n>>m)
{
bool flag = true; // 判断能否完成任务
priority_queue<node, vector<node> > que;
// 读入并处理钉子与绳子的数据
cin>>point[1];
For(i, 2, n)
{
cin>>point[i];
que.push({i-1, i, point[i]-point[i-1]});
}
For(i, 1, m) cin>>rope[i];
// 处理钉子
For(i, 1, m)
{
if(que.empty()) //如果已经没有钉子可挂绳子
{
flag = false;
break;
}
node tmp = que.top();
want[i] = tmp.dis; // 记录每个距离
if(tmp.R+1 <= n)
{
tmp.dis += (point[tmp.R+1]-point[tmp.R]);
tmp.R += 1;
que.push(tmp); // 处理得到从当前点到尾点开始的所有线段
}
que.pop();
}
sort(rope+1, rope+1+m);
if(flag) // 钉子数量足够挂上绳子
For(i, 1, m)
if(rope[i] < want[i]) // 绳子长度短于钉子间距离
{
flag = false;
break;
}
if(flag) cout<<"yes"<<endl;
else cout<<"no"<<endl;
}
return 0;
}