A. Game 23
乘2和乘3的次数显然不会太多,暴力dfs即可。
代码:
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
using namespace std;
int n,m,ans;
void dfs(int now,int cnt){
if(now > m) return;
if(now == m){
ans = min(ans,cnt);
return;
}
dfs(now*2,cnt+1);
dfs(now*3,cnt+1);
}
int main(){
ans = INT_MAX;
cin >> n >> m;
dfs(n,0);
if(ans != INT_MAX) cout << ans;
else cout << -1;
return 0;
}
B. Maximal Continuous Rest
复制一倍接到后面然后找最多连续的1就好了。
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
using namespace std;
const int N = 5e5+10;
int n,a[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
int ans = 0;
fir(i,1,n){
cin >> a[i],a[i+n] = a[i];
}
int now = 0;
fir(i,1,2*n){
if(a[i]){
now ++ ;
ans = max(ans,now);
}
else now = 0;
}
cout << ans;
return 0;
}
C. Polycarp Restores Permutation
前缀和和差分互为逆运算,这题只需要求一遍前缀和,然后判断一下前缀和数组的最小值是大于0还是小于0,如果大于0那么直接把整个数组加上最小值,小于0的话就加上abs(最小值)+1.然后判断数组是否是一个排列即可。
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
using namespace std;
const int N = 5e5+10;
int n,a[N],vis[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
int mx = INT_MAX;
bool f = 1;
fir(i,1,n-1){
cin >> a[i];
if(!a[i]) f = 0;
a[i] += a[i-1];
mx = min(mx,a[i]);
}
if(mx < 0) mx = -mx + 1;
afir(i,n,1) a[i] = a[i-1] + abs(mx);
fir(i,1,n){
if(a[i] < 1 || a[i] > n || vis[a[i]]){
f = 0;
break;
}
vis[a[i]] = 1;
}
if(!f) cout << -1;
else fir(i,1,n) cout << a[i] << " ";
return 0;
}
D. Colored Boots
小型模拟题,按题意把符合的二元对分为以下几类:
- 正常的字母对字母
- a的‘?’对b的剩余字母
- b的‘?’对a的剩余字母
- a的剩余’?‘对b的剩余’?
然后用一个vector或者数组存起来最后输出即可。
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
using namespace std;
const int N = 5e5+10;
int n;
string a,b;
vector<int> va[26],vb[26];
vector<pair<int,int> > ans;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> a >> b;
a = ' ' + a;
b = ' ' + b;
vector<int> vec1,vec2;
fir(i,1,n){
if(a[i] != '?') va[a[i]-'a'].pb(i);
else vec1.pb(i);
if(b[i] != '?') vb[b[i]-'a'].pb(i);
else vec2.pb(i);
}
fir(i,0,25){
int len = (int)min(va[i].size(),vb[i].size());
fir(j,0,len-1)
ans.pb(make_pair(va[i][j],vb[i][j]));
reverse(va[i].begin(),va[i].end());
reverse(vb[i].begin(),vb[i].end());
fir(j,0,len-1) va[i].pop_back(),vb[i].pop_back();
}
fir(i,0,25){
while(vec2.size()){
if(!va[i].size()) break;
ans.pb(make_pair(va[i].back(),vec2.back()));
va[i].pop_back();vec2.pop_back();
}
}
fir(i,0,25){
while(vec1.size()){
if(!vb[i].size()) break;
ans.pb(make_pair(vec1.back(),vb[i].back()));
vb[i].pop_back();vec1.pop_back();
}
}
while(vec1.size() && vec2.size()){
ans.pb(make_pair(vec1.back(),vec2.back()));
vec1.pop_back(),vec2.pop_back();
}
cout << ans.size() << "\n";
for(auto x:ans) cout << x.first << " " << x.second << "\n";
return 0;
}
E. Superhero Battle
对数组求一遍前缀和数组a,再用一个mx记录前缀和的最小值。如果H+mx>0,并且a[n]>=0,那么无解。否则我们只需要循环(H-mx)/sum[n]轮,并且在下一轮里当H+mx<=0的时候输出答案即可。这题更多的是细节上面的问题,这里的讲解知识大致的思路,具体实现可以看代码
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
#define LL long long
using namespace std;
const int N = 5e5+10;
LL h,a[N];
int n;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> h >> n;
LL mx = 1e16,id = 1;
fir(i,1,n){
cin >> a[i];
a[i] += a[i-1];
if(a[i] < mx){
mx = a[i];
id = i;
}
}
if(h + mx > 0 && a[n] >= 0){
cout << -1;
return 0;
}
if(h+mx <= 0){
fir(i,1,n){
if(a[i]+h<=0){
cout << i;
return 0;
}
}
}
LL ned = (h+mx)/abs(a[n]);
if((h+mx)%abs(a[n])) ned++;
LL ans = ned*n;
h += ned*a[n];
fir(i,1,n){
if(h + a[i] <= 0){
cout << ans + i;
return 0;
}
}
cout << ans;
return 0;
}
F1. Same Sum Blocks (Easy)
F2. Same Sum Blocks (Hard)
无论是Easy还是Hard,n都不会很大,所以产生的区间对也最多是15002的,那么可以按照这些区间对的权值和分类,然后在每一类中找到最大的分割方式即可。
找这个最大分割方式在Easy中可以和求LIS一样O(n2)求解,整体复杂度就是O(n3)的,Hard中可以用线段树或者前缀最大值优化一层转移,变成O(n2log(n))或者是O(n2)。做题的时候没反应过来用一个前缀最大值就可以解决,看了题解才想到不用线段树那么麻烦,所以代码实现里用的是线段树的方法。
还有就是输出方案的问题,这是dp很常见的回溯的套路。具体实现看代码吧。
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
#define LL long long
#define pii pair<int,int>
#define mpr make_pair
using namespace std;
const int N = 2e6+10;
const int M = 3e6+10;
int n,a[N],dp[M];
vector<int> all;
int getpos(int x){
return lower_bound(all.begin(),all.end(),x) - all.begin();
}
vector<pii> vec[M];
bool cmp(pii a,pii b){
return a.second < b.second;
}
namespace seg{ // 单点更新 区间求最值
#define ls 2*i
#define rs 2*i+1
int mx[N];
void update(int i,int l,int r,int p,int v){
if(l == r) return void(mx[i] = v);
int mid = l + r >> 1;
if(p <= mid) update(ls,l,mid,p,v);
else update(rs,mid+1,r,p,v);
mx[i] = max(mx[ls],mx[rs]);
}
int ask(int i,int l,int r,int L,int R){
if(l >= L && r <= R) return mx[i];
int mid = l + r >> 1;
if(R <= mid) return ask(ls,l,mid,L,R);
else if(L > mid) return ask(rs,mid+1,r,L,R);
else return max(ask(ls,l,mid,L,mid),ask(rs,mid+1,r,mid+1,R));
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
fir(i,1,n){
cin >> a[i];
a[i] += a[i-1];
fir(j,1,i) all.pb(a[i]-a[j-1]);
}
sort(all.begin(),all.end());
all.erase(unique(all.begin(),all.end()),all.end());
fir(i,1,n){
fir(j,1,i){
vec[getpos(a[i]-a[j-1])].pb(mpr(j,i));
}
}
int tn = all.size();
int ans = 0,id = 0;
fir(i,0,tn-1){
vec[i].pb(mpr(INT_MIN,INT_MIN));
sort(vec[i].begin(),vec[i].end());
int nn = vec[i].size()-1;
fir(j,1,nn){
int newval = seg::ask(1,0,1501,0,vec[i][j].first-1) + 1;
seg::update(1,0,1501,vec[i][j].second,newval);
if(seg::mx[1] > ans){
ans = seg::mx[1];
id = i;
}
}
fir(j,1,nn) seg::update(1,0,1501,vec[i][j].second,0);
}
cout << ans << "\n";
int nn = vec[id].size()-1,idx=1;
fir(i,1,nn){
int newval = seg::ask(1,0,1501,0,vec[id][i].first-1) + 1;
seg::update(1,0,1501,vec[id][i].second,newval);
dp[i] = newval;
if(dp[i] == ans) idx = i;
}
while(ans){
cout << vec[id][idx].first << " " << vec[id][idx].second << "\n";
ans--;
afir(i,idx-1,1){
if(dp[i] == ans && vec[id][idx].first > vec[id][i].second){
idx = i;
break;
}
}
}
return 0;
}
G. Privatization of Roads in Treeland
看似是一道图论题实际上是一个思维题,思考一下可以发现,假设用x个公司,那么一个节点的边如果超过了x条,那么这个节点一定会觉得不公平。所以只需要二分一下公司的数量,然后判断不公平节点的个数是否超过k即可。
比较麻烦的是染色的问题,我的染色方案是子节点从父亲节点颜色+1开始染,这种方案大概是不会冲突的。(瞎搞A了)
代码:
#include <bits/stdc++.h>
#define fir(i,a,b) for(int i = a;i <= b; ++ i)
#define afir(i,a,b) for(int i = a;i >= b; -- i)
#define pb push_back
#define LL long long
#define pii pair<int,int>
#define mpr make_pair
using namespace std;
const int N = 4e5+10;
int ans,n,k,ct,head[N],to[N],nxt[N],bel[N],col[N],num[N];
void addedge(int x,int y,int i){
nxt[++ct] = head[x];head[x] = ct;bel[ct] = i;to[ct] = y;
}
void dfs(int u,int fa,int tot,int cnt){
for(int i = head[u];i;i = nxt[i]){
int y = to[i],bl = bel[i];
if(y == fa) continue;
col[bl] = ++ cnt;
if(cnt == tot) cnt = 0;
dfs(y,u,tot,cnt);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> k;
fir(i,1,n-1){
int x,y;
cin >> x >> y;
addedge(x,y,i);
addedge(y,x,i);
num[x]++;num[y]++;
}
int l = 1,r = n;
while(l < r){
int mid = l + r >> 1;
int nu = 0;
fir(i,1,n) if(num[i] > mid) nu++;
if(nu <= k) r = mid;
else l = mid+1;
}
cout << l << "\n";
dfs(1,0,l,0);
fir(i,1,n-1) cout << col[i] << " ";
return 0;
}