C#查詢演算法2:插值查詢
- A. Robot Program
- B. Toy Blocks
- C. Two Brackets
- D. Radio Towers
- E. Two Editorials
- F. Divide Powers
- G. Game On Tree
A. Robot Program
題意:
一個機器人要從\((0,0)\)位置走到\((x,y)\)位置,每次可以進行5種操作,分別是向上下左右方向走一格,或者是待在原地。機器人不能連續進行同樣的兩次操作(例如不能連續向右走兩次,但是可以走一次,停在原地一次,然後再走一次),問最少進行多少次操作可以到達目的地
思路:
當\(x=y\)或者x與y的差只有1時,可以一直走,不需要停,否則必須要在中途停下來。
#include<bits/stdc++.h> using namespace std; const int N = 1e6 + 5; int t, x, y; int main(){ cin>>t; while(t--){ cin >> x >> y; if(abs(x-y)<=1){ cout << x + y << endl; } else{ cout << min(x, y) * 2 + 1 + 2 * (max(x, y) - (min(x, y) + 1)) << endl; } } return 0; }
B. Toy Blocks
題意:
有 n 個盒子,每個盒子有 \(a_i\) 個糖果。現在你可以向一些盒子中新增糖果,使得任選一個盒子,將其中的糖果分到其餘 n-1 個盒子中,每個盒子的糖果數量相等。問最少需要新增多少糖。
思路
直接暴力做,對於每個點,判斷一下把其他糖果填平的糖果即可,這裡的填平是需要將其他的糖果補充到他們的最大值,然後再看剩下的糖果數量,判斷是否還需要再填一層
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int t, n, x, a[N]; long long anss; long long sum,maxn=-1,minn=0x3f3f3f3f; int main(){ cin >> t; while(t--){ cin >> n; sum = 0; anss = 0; for (int i = 0; i < n;i++){ cin >>a[i]; sum += a[i]; } sort(a, a + n); maxn = a[n - 1]; minn = a[0]; for (int i = 0; i < n;i++){ minn = a[i]; if(i==n-1){ maxn = a[i - 1]; } if (maxn * (n - 1) - (sum - minn) > minn) { anss=max(anss, maxn * (n - 1) - (sum - minn) - minn ); } else if (maxn * (n - 1) - (sum - minn) > minn) { anss = max(anss, (long long)0); } else{ if(((minn-(maxn*(n-1)-(sum-minn)))%(n-1)==0)){ anss = max(anss, (long long)0); } else{ anss = max(anss, (n - 1) - (minn - (maxn * (n - 1) - (sum - minn))) % (n - 1)); } } } cout << anss << endl; } return 0; }
C. Two Brackets
題意:
計算有多少配對的括號
思路:
記錄一下前面出現的左括號lsum,當遇到右括號時,如果lsum大於0,就將lsum--,然後答案++
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int t,lnum1,lnum2,sum;
string s;
int main(){
cin>>t;
while(t--){
cin >> s;
sum = 0;
lnum1 = 0;
lnum2 = 0;
for (int i = 0;i<s.size();i++){
if(s[i]=='('){
lnum1++;
}
else if(s[i]==')'){
if(lnum1){
lnum1--;
sum++;
}
}
else if(s[i]=='['){
lnum2++;
}
else if(s[i]==']'){
if(lnum2){
lnum2--;
sum++;
}
}
}
cout << sum << endl;
}
return 0;
}
D. Radio Towers
題意:
一共0到n+1共n+2個點,每個點上都可以放置或者不放置一個訊號塔,訊號塔的輻射範圍是1-n,每個點只能被一個訊號塔覆蓋,0號和n+1號點不能被覆蓋,問符合條件的放置方法佔全部的多少
思路:
找規律即可,分子為斐波那契數列,分母為\(2^n\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
const int mod = 998244353;
typedef long long LL;
LL n,fz,fm,fib[N];
LL get_fz(LL n){
fib[1] = 1;
fib[2] = 1;
for (int i = 2; i <= n;i++){
fib[i] = ((LL)fib[i - 1] + (LL)fib[i - 2]) % mod;
}
return fib[n];
}
LL qmi(LL a, LL k, LL p) {
LL res = 1 % p; // res記錄答案, 模上p是為了防止k為0,p為1的特殊情況
while(k) { // 只要還有剩下位數
if (k & 1) res = (LL)res * a % p; // 判斷最後一位是否為1,如果為1就乘上a,模上p, 乘法時可能爆int,所以變成long long
k >>= 1; // 右移一位
a = (LL) a * a % p; // 當前a等於上一次的a平方,取模,平方時可能爆int,所以變成long long
}
return res;
}
LL get_inv(LL a, LL p) {
return a % p == 0? -1: qmi(a, p - 2, p);
}
LL get_fm(LL n){
LL temp = qmi(2,n,mod);
return get_inv(temp, mod);
}
int main(){
cin>>n;
fz = get_fz(n);
fm = get_fm(n);
cout << fz * fm % mod << endl;
return 0;
}
E. Two Editorials
大意:
一共有m個位於[1,n]的區間p,現在有長度為K的區間b和c。設對於區間p[i],定義a[i]為p[i]分別與b,c相交長度的較大值,現在問區間b和c位於何處時,a[i]的和最大
思路:
純暴力肯定不行,所以需要考慮對其進行優化,因為對於區間p,如果它的中點在b和c的中點的左側,那麼一定是對b產生貢獻,反之則是對c產生貢獻,那麼對於每個點i,p個區間都可以分為兩部分,前一部分對b產生貢獻,後一部分對c產生貢獻,所以首先對m個p區間按照中點排序,然後遍歷n,找到對於每個點i的分界點,即\(pre[i]\)代表有多少區間的中點在i的左側。然後跑一遍字首和,\(sum[i][j]\)代表對於位置i,前j個區間可以產生的貢獻,最後暴力列舉區間b和c,算一下兩個區間獲得的貢獻即可。這樣複雜度為\(O(n*m)\)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;
int n, m, k,pre[N],sum[N][N];
struct node
{
int l, r;
}a[N];
bool cmp(node a,node b){
return (a.l + a.r < b.l + b.r);
}
int main(){
cin>>n>>m>>k;
for (int i = 0; i < m;i++){
cin >> a[i].l >> a[i].r;
}
sort(a, a + m, cmp);
int j = 0;
for (int i = 1; i <= n;i++){
while(j<m&&(a[j].l+a[j].r<=2*i)){
j++;
}
pre[i] = j-1;
}
for (int i = 1; i <= n - k + 1;i++){
int l = i, r = i + k - 1; //當前區間的左右範圍
for (int j = 1; j <= m;j++){
sum[i][j] = sum[i][j - 1] + max(0, min(a[j - 1].r, r) - max(a[j - 1].l,l )+1);
}
}
int res = 0;
for (int i = 1; i <= n - k + 1;i++){
for (int j = i; j <= n - k + 1;j++){
int mid = (i + j + k - 1) >> 1;
res = max(res, sum[i][pre[mid] + 1] + sum[j][m] - sum[j][pre[mid] + 1]);
}
}
cout << res << endl;
return 0;
}
F. Divide Powers
題意:
給出一個數組\(cnt_i\)代表當前有\(cnt_i\)個\(2^i\),當\(i>0\)時,可以將一個\(2^i\)變成兩個\(2^{i-1}\),現在又兩個操作,一類是修改\(cnt_i\),另一類是詢問進行多少次修改後會有不少於\(k\)個元素小於等於\(2^x\)
思路:
對於大於\(2^x\)的元素,將其修改到\(2^x\),可以比修改小於\(2^x\)的元素要多出一個,所以優先修改大於\(2^x\)的元素,但是如果修改到\(2^x\)獲得的元素比需要的多,那麼需要判斷是否修改小於\(2^x\)的元素,也就是需要記錄當前已經選擇的元素,讓他們全部修改為\(2^0\)可以獲得的元素個數。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
LL n, q, cnt[35],op,a[35];
int main(){
cin >> n >> q;
a[0] = 1;
for (int i = 0; i < n;i++){
cin >> cnt[i];
if(i){
a[i] = a[i - 1] * 2;
}
}
while(q--){
cin >> op;
if(op==1){
LL pos, val;
cin >> pos >> val;
cnt[pos] = val;
}
else{
LL x, k,sum=0,res=0;
cin >> x >> k;
for (int i = 0; i <= x;i++){
k -= cnt[i];
sum += (a[i]-1) * cnt[i]; //記錄當前選擇的還可以多產生多少符合條件的值
}
if(k<=0){
cout << 0 << endl;
continue;
}
for (int i = x + 1; i < n;i++){
if((a[i-x]*cnt[i])<=k){
k -= a[i - x] * cnt[i];
res += (a[i - x] - 1) * cnt[i];
sum += (a[i] - a[i - x]) * cnt[i];
}
else{
int p=k/a[i-x];
k%=a[i-x];
sum+=p*(a[i]-a[i-x]);
res+=p*(a[i-x]-1);
for (int j=i;j>=x;j--)
{
if (k<=sum)
{
res+=k;
k=0;
break;
}
if (a[j-x-1]<=k)
{
k-=a[j-x-1];
sum+=a[j-1]-a[j-x-1];
res+=a[j-x-1];
}
else res++;
}
break;
}
}
if(k<=sum){
cout << res + k << endl;
}
else
cout << -1 << endl;
}
}
return 0;
}
G. Game On Tree
大意:
Alice 和 Bob 在玩一個遊戲。他們有一棵由 n 個結點組成的樹。一開始,Bob 有 k個卡片,其中第i個卡片位於結點 \(a_i\),在遊戲開始之前,Alice 將在這棵樹的一個結點上放置一個卡片。
這個遊戲由一些回合組成。每個回合都將有以下事件發生(完全按照以下順序):
- Alice 可以把她的卡片移到相鄰的結點,或者不移動;
- 對於 Bob 的每一張卡片,他可以把這張卡片移到相鄰的結點,或者不移動。注意:每個卡片的選擇都是獨立的。
當 Alice 的卡片與 Bob 的任意一張(或多張)卡片在同一結點時,遊戲結束。(Bob 自己的多張卡片可以置於同一結點上,即使它們的初始位置一定是不同的)。
Alice 希望遊戲回合越多越好,Bob則相反。如果某回合中間遊戲結束(即 Alice 把卡片移到了有 Bob 卡片的結點上),這回合依然算入總回合數。
對於每個結點,計算 Alice 一開始將卡片放在該結點時遊戲將持續的回合數。
思路:
首先利用拓撲排序求出來每個點距離Alice的最近的一個卡片的距離\(dis[i]\),對於Bob來說,必然是想前往\(dis[x]\)較大的點,而對於Alice來說,必然是所有的點都向Bob逼近,得到\(dis[x]\)陣列後,可以從最大的值開始更新周圍的點的答案,但是這樣複雜度會很高,所以引入\(h[i]\)陣列,代表上一次利用i點更新周圍點的答案時,i點距離最近的一個“敵人”的距離,所以只有噹噹前距離最近的"敵人"的距離大於\(h[i]\)時,才利用這個點更新答案,否則不需要更新。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m,res[N],dis[N],maxn,h[N];
vector<int> mp[N],D[N];
queue<int>q;
int main(){
cin >> n;
for (int i = 1; i < n;i++){
int u,v;
cin >> u >> v;
mp[u].push_back(v);
mp[v].push_back(u);
dis[i] = -1;
}
dis[n] = -1;
cin >> m;
while(m--){
int x;
cin >> x;
dis[x] = 0;
q.push(x);
}
while(!q.empty()){
int u = q.front();
q.pop();
for (int i = 0; i < mp[u].size();i++){
int v = mp[u][i];
if(dis[v]>=0)
continue;
dis[v] = dis[u] + 1;
q.push(v);
}
D[dis[u]].push_back(u);
maxn = max(maxn, dis[u]);
}
queue<pair<int, int>> q1;
for (int i = maxn; i > 0;i--){
for (int j = 0; j < D[i].size();j++){
int u = D[i][j];
if(h[u]<dis[u]){
if(res[u]==0){
res[u] = i;
}
h[u] = dis[u];
q1.push({dis[u],u});
}
while(!q1.empty()){
int d = q1.front().first;
int u = q1.front().second;
q1.pop();
if(--d==0)
continue; //更新過來直接是敵人
for(int j = 0;j<mp[u].size();j++){
int v = mp[u][j];
int temp = min(d, dis[v]);
if(temp>h[v]){
if(res[v]==0)
res[v] = i;
h[v] = temp;
q1.push({temp, v});
}
}
}
}
}
for (int i = 1; i <= n;i++){
cout << res[i] << ' ';
}
cout << endl;
return 0;
}