A题-Red Versus Blue
思路
- 尽可能使得R平均在B的空隙中分配就好
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<algorithm>
#include<map>
#include<queue>
#include <chrono>
#include<math.h>
#include<unordered_map>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
for(int o = 0;o<t;o++)
{
int n,r,b;
cin >> n >> r >> b;
int each = r / (b+1);
int add = r - r / (b+1) * (b+1);
string s = "";
int i;
for( i = 0;i<add;i++)
{
for(int k = 0;k<each+1;k++) s+="R";
s += "B";
}
for(;i<b+1;i++)
{
for(int k = 0;k<each;k++) s+="R";
s+="B";
}
s = s.substr(0,s.size() - 1);
cout << s << endl;
}
system("pause");
return 0;
}
B题(BIT FLIPPING)
思路
- 分k是奇数,k是偶数讨论。
- 贪心:尽量使得高位为1
- 剩余的次数累加到最后
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<algorithm>
#include<map>
#include<queue>
#include <chrono>
#include<math.h>
#include<unordered_map>
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin >> t;
for(int o = 0;o<t;o++)
{
int n,k;
cin >> n >> k;
string s;
int k1 = k;
cin >> s;
int ans[n];
memset(ans,0,n*sizeof(int));
int pre_filp = 0;
for(int i = 0;i<s.size();i++)
{
if(k==0) break;
char c;
if(pre_filp %2 == 0) c = s[i];
else if(s[i] == '1') c = '0';
else if(s[i] == '0') c = '1';
if(c == '0')
{
if(k%2==0) {pre_filp++;ans[i]++;k--;}
}
if(c == '1')
{
if(k%2!=0) {pre_filp++;ans[i]++;k--;}
}
}
ans[n-1]+=k;
string a;
for(int i = 0;i<n;i++)
{
if(((k1 - ans[i])) % 2 ==0) a += s[i];
else
{
if(s[i] == '0') a+="1";
else a += "0";
}
}
cout << a << endl;
for(int i = 0;i<n;i++)
{
cout << ans[i];
if(i < n - 1) cout << " ";
}
cout << endl;
}
system("pause");
return 0;
}
C题(Line Empire)
思路
- 枚举首都最后的位置,然后贪心计算。
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<algorithm>
#include<map>
#include<queue>
#include <chrono>
#include<math.h>
#include<unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
cin >> t;
for(int i = 0;i<t;i++)
{
ll n,a,b;
cin >> n >> a >> b;
ll x[n+1];
x[0] = 0;
for(int k = 0;k<n;k++)
{
cin >> x[k+1];
}
ll pre[n+1];
pre[n] = 0;
for(int k = n-1;k>=0;k--)
{
pre[k] = pre[k+1] + (n - k) * (x[k+1] - x[k]);
}
ll ans = LONG_LONG_MAX;
for(int k = 0;k<n+1;k++)
{
ans = min(ans,pre[k] * b + (a+b) * (x[k] - x[0]));
}
cout << ans << endl;
}
system("pause");
return 0;
}
D题(Reverse Sort Sum)合理构建子问题GOOD
思路(从后往前更简单)
- 从前往后(复杂):分类讨论,从第一个数开始考虑。考察后面多加多少个1才能使得和等于目标值。思路较模糊
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
cin >> t;
for (int i = 0; i < t; i++)
{
int n;
cin >> n;
int c[n];
for (int k = 0; k < n; k++)
cin >> c[k];
vector<int> ans;
int k = 0;
int pre_0 = 0;
while (k < n && c[k] == 0)
{
ans.push_back(0);
pre_0++;
k++;
}
if (k < n)
ans.push_back(1);
//后面的每个和都不可能为0
if (k < n)
{
int q = c[k] - k - 1;
int p = 0;
while (p < q)
{
ans.push_back(1);
p++;
}
ans.push_back(0);
p = k + 1;
q = ans.size() - 1;
while (p < n && q < n && ans.size() < n)
{
int remain = c[p] - ans[p] * p - 1;
int l = 0;
int m = q - p;
while (l < remain - m)
{
ans.push_back(1);
q++;
l++;
}
if (ans.size() < n)
{
ans.push_back(0);
q++;
}
p++;
}
}
k = 0;
while (k < n - 1)
{
cout << ans[k] << " ";
k++;
}
cout << ans[n - 1] << endl;
}
system("pause");
return 0;
}
当时考虑过从后往前做,但没有继续。
- 从后往前考虑更简单。先确定最后一个是0还是1,根据最后一步排序格局**确定前n-1个数构成的sum集合,在执行完 f ( A , 1 ) , f ( A , 2 ) ⋯ f ( A , n − 1 ) f(A,1),f(A,2) \cdots f(A,n-1) f(A,1),f(A,2)⋯f(A,n−1)的前提下。使用和上一道progression(也是D题)相同的思路即可。
- 再考虑前n-1个数构成的子问题。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
cin >> t;
for(int i = 0;i<t;i++)
{
int n;
cin >> n;
int c[n];
vector<int> ans;
ll sum = 0;
for(int k = 0;k<n;k++) {cin >> c[k];sum+=(ll)c[k];}
int one = (int)((sum) / n);
int predec = 0;
int f[n];
memset(f,0,n*sizeof(int));
while(n>1)
{
if(c[n-1] - predec <=1) {ans.push_back(0);} else {ans.push_back(1);}
if(one - 1 >= 1)
{
predec++;
if(n - one >= 0) f[n-one]++;
}
one-=ans[ans.size() - 1];
predec-=f[n-1];
n--;
}
if(c[0] - predec >=1) ans.push_back(1); else ans.push_back(0);
one -= ans[ans.size() - 1];
for(int k = ans.size() - 1;k>=0;k--)
{
cout << ans[k];
if(k > 0) cout << " ";
}
cout << endl;
}
system("pause");
return 0;
}
总结
- 本质上还是对问题理解不够深入。选择从后往前是因为可以快速确定最后一步的格局,充分利用sum数组的总和等于原始数据中1的个数乘以n
- 而从前往后没有这个性质。
E. AND-MEX Walk
思路
- 解空间的缩小:显然 M E X < = 2 MEX<=2 MEX<=2
- 讨论每种情况:MEX=0,MEX=1,MEX=2;
- 若MEX=0,路径上的数需要至少一位bit恒保持1.
- 若MEX=1,路径上前面几个数为至少从低位开始第二个bit为1的数,然后突变为0;
- 对固定bit为1对应的所有边,使用并查集维护这些边构成的连通分量。
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include <map>
#include <queue>
#include <chrono>
#include <math.h>
#include <unordered_map>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int uf[30][100000]; //记录并查集信息
int h[30][100000];
int w[30][100000];
int o[30][100000];
int find_set(int x,int s[])
{
if(x != s[x]) s[x]=find_set(s[x],s);
return s[x];
}
void union_set(int x,int y,int s[],int h[],int w[])
{
x = find_set(x,s);
y = find_set(y,s);
if(x == y) return;
if(h[x] == h[y])
{
h[x] = h[y] + 1;
s[y] = x;
w[x] = (w[y] & w[x]);
}
else
{
if(h[x] < h[y]) {s[x] = y;w[y] = (w[y] & w[x]);}
else {s[y] = x;w[x] = w[y] & w[x];}
}
}
int main()
{
int n,m;
cin >> n >> m;
vector<pair<int,int>> adj[n]; //每个顶点的邻接表
vector<array<int,3>> edge;
for(int i = 0;i<m;i++)
{
int a,b,w;
cin >> a >> b >> w;
a--; b--;
adj[a].push_back(make_pair(b,w));
adj[b].push_back(make_pair(a,w));
edge.push_back({a,b,w});
}
//map<int,int> o[30];
for(int u = 0 ; u<30;u++)
{
//memset(w[u],- 1,n*sizeof(int));
for(int i = 0;i<n;i++) //初始化每个结点
{
w[u][i] = (1 << 30) - 1;
// cout << w[u][i] << endl;
h[u][i] = 0;
uf[u][i] = i; //每个结点父亲是其自身
}
for(array<int,3> a:edge)
{
if((a[2] >> u) & 1)
{
union_set(a[0],a[1],uf[u],h[u],w[u]);
w[u][find_set(a[0],uf[u])] &= a[2];
}
}
for(int i = 0;i<n;i++) //对于每个结点遍历邻接表
{
int x = find_set(i,uf[u]); //找所属集合
for(pair<int,int> b : adj[i])
{
if(u > 0 && (w[u][x] & b.second) == 0) //与该条边相与结果为0
o[u][x]++;
}
}
}
int q; cin >> q;
for(int i = 0;i<q;i++)
{
int u,v;
cin >> u >> v;
u--;v--;
int flag = 3;
for(int k = 0;k<30;k++)
{
//是否可能为0
int u_s = find_set(u,uf[k]);
int v_s = find_set(v,uf[k]);
if(u_s == v_s)
{
flag = min(flag,1);
break;
}
else if((o[k][u_s] > 0 ))
{
flag = min(flag,2);
}
}
if(flag == 3) cout << 2 << endl;
else if(flag == 2) cout << 1 << endl;
else cout << 0 << endl;
}
system("pause");
return 0;
}