大一寒假集訓-線上集訓第一次考試
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;
}