Hint
- Bạn có thể sử dụng phương pháp Sinh/Quay lui hoặc dùng hàm
next_permutation
có sẵn để có thể sinh ra đủ $$$n!$$$ hoán vị.
- Sau đó, nếu số đầu tiên lớn hơn số cuối cùng thì ta sẽ in ra hoán vị đó.
Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define el '\n'
void solve(){
int n; cin >> n;
vector<int> v;
for(int i=1; i<=n; i++) v.push_back(i);
do{
if(v[0] > v[n - 1]){
for(int &i : v) cout << i << ' '; cout << el;
}
}while(next_permutation(v.begin(), v.end()));
}
signed main(){
cin.tie(0) -> sync_with_stdio(0);
int tc = 1; //cin >> tc;
while(tc--) solve();
}
Hint
- Với giới hạn lên tới $$$10^6$$$, việc duyệt qua từng số rồi tìm ước nguyên tố lớn nhất là không thể, vì ta sẽ mất $$$O(\sqrt n)$$$ để tìm ước, tức là xấp xỉ $$$10^3$$$ $$$\to$$$ tổng độ phức tạp sẽ là $$$O(10^9)$$$, vượt quá $$$1$$$ giây cho phép.
- Để có thể tối ưu được thời gian, ta sẽ cần sử dụng Sàng Biến Đổi ở đây. Đầu tiên, ta sẽ duyệt qua các số trong đoạn $$$[2, 10^6]$$$, với mỗi số $$$i$$$ đang xét tới, ta sẽ duyệt các bội của nó và cập nhật ước nguyên tố lớn nhất hiện tại chính là $$$i$$$. Tiếp theo, các truy vấn tính tổng tĩnh trong đoạn $$$[l, r]$$$ nào đó, ta sẽ phải nghĩ ngay đến Tổng cộng dồn để tiếp tục tối ưu thời gian, chứ không thể duyệt trâu các truy vấn được.
Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define el '\n'
int d[1000005];
long long f[1000005];
void pre(){
for(int i=2; i<=1e6; i++){
if(d[i] == 0){
for(int j=i; j<=1e6; j+=i){
d[j] = i;
}
}
}
for(int i=2; i<=1e6; i++){
f[i] = f[i - 1] + d[i];
}
}
void solve(){
int l, r; cin >> l >> r;
cout << f[r] - f[l - 1] << el;
}
signed main(){
cin.tie(0) -> sync_with_stdio(0);
pre();
int tc = 1; cin >> tc;
while(tc--) solve();
}
Hint
- Xâu $$$s$$$ có thể có dấu cách nên ngoài các cách nhập xâu có dấu cách mà bạn có thể đã biết, thì mình có $$$1$$$ cách sau đây cũng khá đơn giản mà cũng có tác dụng tương tự
getline(cin >> ws, s)
thìws
ở đây mình nghĩ có nghĩa làwhitespace
, thì nó cũng giúp việc cin xong getline không bị lỗi. Tuy nhiên, nếu không có cin trước cái getline này, thì mình không khuyến khích các bạn sử dụng vì nó có thể gây lỗi.
- Ở bài toán này, ta sẽ sử dụng ngăn xếp để xử lý. Về cơ bản, phần thuật sẽ khá giống với bài kiểm tra dãy ngoặc đúng, chỉ khác ở chỗ khi gặp dấu
(
ta sẽ in ra số thứ tự nó xuất hiện và đẩy số thứ tự đó vào stack (mục đích là để khi gặp dấu đóng ngoặc tương ứng, ta cũng sẽ in ra được số thứ tự này). Như vậy, khi gặp dấu)
thì ta chỉ cần in ra số ở đầu stack và pop nó ra.
Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define el '\n'
void solve(){
string a; getline(cin >> ws, a);
int n = a.size();
stack<int> st;
int x = 1;
for(int i=0; i<n; i++){
if(a[i] == '('){
cout << x << ' ';
st.push(x);
++x;
}else if(a[i] == ')'){
cout << st.top() << ' ';
st.pop();
}
}
cout << el;
}
signed main(){
cin.tie(0) -> sync_with_stdio(0);
int tc = 1; cin >> tc;
while(tc--) solve();
}
583169D - Điểm nghẽn giao thông
Hint
- Ta có thể dễ dàng nhận ra rằng các điểm nghẽn, khi ta bỏ chúng ra khỏi đồ thị, thì việc đi từ $$$x \to y$$$ là hoàn toàn bất khả thi. Như vậy, ta sẽ duyệt qua các đỉnh từ $$$1 \to n$$$, ta sẽ đánh dấu
vis[i] = 1
để khi chạy từ $$$x$$$ đến $$$y$$$ nó sẽ không đi qua được $$$i$$$ nữa. Nếu $$$x$$$ vẫn đến được $$$y$$$ thì chứng tỏ $$$i$$$ không phải điểm nghẽn, ngược lại thì $$$i$$$ là điểm nghẽn.
Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define el '\n'
int n, m, s, t;
vector<int> a[101];
bool vis[101];
void dfs(int u){
vis[u] = 1;
for(int &v : a[u]){
if(!vis[v]) dfs(v);
}
}
void solve(){
cin >> n >> m >> s >> t;
while(m--){
int x, y; cin >> x >> y;
a[x].push_back(y);
}
int res = 0;
for(int i=1; i<=n; i++){
memset(vis, 0, sizeof vis);
vis[i] = 1;
dfs(s);
if(!vis[t]) ++res;
}
cout << res << el;
for(int i=1; i<=n; i++) a[i].clear();
}
signed main(){
cin.tie(0) -> sync_with_stdio(0);
int tc = 1; cin >> tc;
while(tc--) solve();
}
Hint
Sau khi nhập xâu $$$s$$$ vào thì ta nên lưu chúng thành vector cho dễ thao tác. Vì cây nhị phân được mô tả bởi xâu $$$s$$$, nên ta sẽ lưu các giá trị node như $$$1$$$ xâu. Về việc xây cây, ta sẽ sử dụng hàng đợi để giải quyết, duy trì thêm biến $$$i$$$ để xác định node cần chèn (bắt đầu từ $$$1$$$ do $$$0$$$ được lấy làm gốc).
Xuất phát từ $$$a_0$$$ được lấy ra ở đầu hàng đợi, ta sẽ gán $$$a_i$$$ và $$$a_{i + 1}$$$ lần lượt là node con bên trái và phải của $$$a_0$$$. Sau đó kiểm tra $$$a_i$$$ và $$$a_{i + 1}$$$ xem nếu không phải node
NULL
(tức là $$$a_i$$$ khác N) thì ta mới push tiếp vào hàng đợi, bởi vìNULL
sẽ không thể có node con.
Code (C++)
#include<bits/stdc++.h>
using namespace std;
#define el '\n'
struct node{
string val;
node *l, *r;
node(string x){
val = x;
l = r = 0;
}
};
void duyet(node *x){
if(!x or x->val == "N") return;
duyet(x->r);
cout << x->val << ' ';
duyet(x->l);
}
void solve(){
string a; getline(cin >> ws, a);
vector<string> v;
stringstream ss(a);
string tmp;
while(ss >> tmp) v.push_back(tmp);
queue<node*> q;
node *root = new node(v[0]);
q.push(root);
int i = 1;
while(!q.empty()){
node *x = q.front(); q.pop();
if(i == v.size()) break;
x->l = new node(v[i]);
if(v[i] != "N") q.push(x->l);
++i;
if(i == v.size()) break;
x->r = new node(v[i]);
if(v[i] != "N") q.push(x->r);
++i;
}
duyet(root); cout << el;
}
signed main(){
cin.tie(0) -> sync_with_stdio(0);
int tc = 1; cin >> tc;
while(tc--) solve();
}