A - Two Permutations
题意
是否存在两个长度为
题解
若
否则当且仅当
代码
signed main(){
fastio();
int T;
cin>>T;
while(T--){
int n, a, b;
cin>>n>>a>>b;
if(n-a-b>1||(a==n&&b==n)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
B - Elimination of a Ring
题意
环上消消乐, 每次操作在环上删去一个数, 然后自动消消乐, 求删去全部元素的最大操作次数.
题解
显然, 当环上元素种类等于
代码
constexpr int N = 1005;
int a[N];
signed main()
{
fastio();
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
Fe(i, 1, n) cin >> a[i];
if (n == 1)
cout << 1 << endl;
else if (n == 2)
cout << 2 << endl;
else
{
bool flag = 0;
for (int i = 3; i <= n; i++)
{
if (a[i - 2] != a[i])
{
flag = 1;
cout << n << endl;
break;
}
}
if (!flag)
cout << n / 2 + 1 << endl;
}
}
return 0;
}
C - Set Construction
题意
构造一些集合, 使得它们满足给定的每两个集合间是否真包含的要求.
题解
诈骗题, 可以直接图论跑, 也可以特殊构造.
考虑矩阵
然后逐个向
最终就可以得到答案, 可以证明这样的构造一定合法.
可以使用 set 直接模拟上述做法.
代码
constexpr int N = 105;
int a[N][N];
set<int> s[N];
inline void solve()
{
int n;
cin >> n;
Fe(i, 1, n) s[i].clear();
Fe(i, 1, n)
{
char c;
Fe(j, 1, n)
{
cin >> c;
a[i][j] = c - '0';
}
}
Fe(i, 1, n) s[i].insert(i);
Fe(i, 1, n)
Fe(j, 1, n)
if (a[i][j] == 1)
for (auto k : s[i])
s[j].insert(k);
Fe(i, 1, n)
{
cout << s[i].size() << ' ';
for (auto k : s[i]) cout << k << ' ';
cout << endl;
}
}
D - Carry Bit
题意
求
题解
枚举段数
有两种可能
第一种是
第二种是 (
Attention: 快速幂实现时不要溢出
代码
constexpr int N = 1000005, mod = 1000000007;
int n,k,pw[N],prd[N],inv[N];
int pow(int x) {
if (x < 0)
return 0;
return pw[x];
}
int C(int n, int m) {
if (n < m)
return 0;
return (ll)prd[n] * inv[m] % mod * inv[n - m] % mod;
}
int qpow(ll b, ll p) {
ll res = 1;
while (p) {
if (p & 1)
res = res * b % mod;
b = b * b % mod;
p >>= 1;
}
return res % mod;
}
signed main() {
fastio();
cin >> n >> k;
pw[0] = prd[0] = 1;
Fe(i, 1, n) pw[i] = 3ll * pw[i - 1] % mod;
if (!k) {
cout<<pw[n]<<endl;
return 0;
}
Fe(i, 1, n) prd[i] = (ll)prd[i - 1] * i % mod;
inv[n] = qpow(prd[n], mod - 2);
for (int i = n - 1; ~i; --i)
inv[i] = (ll)inv[i + 1] * (i + 1) % mod;
ll ans = 0;
Fe(i, 1, k) {
ans += (ll)C(k - 1, i - 1) * C(n + 1 - k - 1, i) % mod *
pow(n - 2 * i) % mod;
ans += (ll)C(k - 1, i - 1) * C(n + 1 - k - 1, i - 1) % mod *
pow(n - 2 * i + 1) % mod;
}
cout << ans % mod << endl;
return 0;
}
E - Make It Connected
题意
有一张无向图, 你可以操作任意次, 每次选定一个点
官方题解
First of all, we need to check if the graph is already connected at the beginning. If so, the answer would be 0.
Otherwise, there will be more than
But actually, there will always be such a vertex in a non-clique component. To prove this, we may figure out the sufficient condition for being a feasible vertex first.
The sufficient condition is that, if a vertex is not a cut vertex, and it is not adjacent to all other vertices in the connected component, then it must be a feasible vertex. We can prove that such a vertex always exists in a non-clique component. Here is the proof:
- Firstly, if there exist vertices that are adjacent to all other vertices in the component, we erase all of them.
Apparently, the remaining component would still be a non-clique component. Otherwise, the component could only be a clique from the beginning, which contradicts the premise.
- Then, we will find a non-cut vertex in the remaining component, since that vertices in a graph couldn't be all cut vertices. The non-cut vertex we found is the vertex we are searching for.
But implementing it directly (meaning using Tarjan's algorithm to find a non-cut vertex) might not be the easiest way to solve the problem. Actually, there are two other ways we have found so far to solve the problem alternatively. The first one is that we can simply output the vertex with the least degree in a connected component; the other one is that we can do DFS in a connected component and output the last visited vertex (as long as the connected component is not a clique). We would like to leave the proof work of these two alternative ways to you.
Now we have proven that, if there exists a connected component that is not a clique, then the answer would be at most
If there are exactly
Otherwise, we can arbitrarily choose two vertices from two different connected components and operate on them. The answer is
Note that we also need to deal with isolated vertices (meaning vertices that are not adjacent to any other vertices) separately.
可以通过找到所有点中度数最小的点找到一个非完全图连通块中的合法操作点.
代码
constexpr int N = 4005;
int n;
int deg[N];
int fa[N];
int c[N];
vector<int> v[N];
vector<int> cv;
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void solve()
{
cin >> n;
Fe(i, 1, n) fa[i] = i, c[i] = 0, deg[i] = 0;
Fe(i, 1, n) Fe(j, 1, n)
{
char o;
cin >> o;
if (o == '1')
{
int x = find(i), y = find(j);
if (x != y)
c[y] += c[x], fa[x] = y;
c[find(j)]++;
deg[i]++;
}
}
cv.clear();
Fe(i, 1, n) v[i].clear();
Fe(i, 1, n) v[find(i)].pb(i);
Fe(i, 1, n) if (fa[i] == i) cv.pb(i);
if (cv.size() == 1)
{
cout << 0 << endl;
return;
}
Fe(i, 1, n) if (fa[i] == i)
{
if (deg[i] == 0 || c[i] != v[i].size() * (v[i].size() - 1))
{
int p = 0;
for (int u : v[i])
if (!p || deg[u] < deg[p])
p = u;
cout << "1\n"
<< p << endl;
return;
}
}
if (cv.size() > 2)
{
cout << "2\n"
<< v[cv[0]].back() << ' ' << v[cv[1]].back() << endl;
return;
}
if (v[cv[0]].size() > v[cv[1]].size())
swap(cv[0], cv[1]);
cout << (int)v[cv[0]].size() << endl;
for (int u : v[cv[0]])
cout << u << ' ';
cout << "\n";
}