# |
Submission time |
Handle |
Problem |
Language |
Result |
Execution time |
Memory |
1051314 |
2024-08-10T04:20:58 Z |
Requiem |
Žarulje (COI15_zarulje) |
C++17 |
|
22 ms |
23192 KB |
#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define fast ios_base::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
#define MOD 1000000007
#define inf 1e18
#define fi first
#define se second
#define endl "\n"
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define FORD(i,a,b) for(int i=a;i>=b;i--)
#define sz(a) ((int)(a).size())
#define pi 3.14159265359
#define TASKNAME "zarulje"
template<typename T> bool maximize(T &res, const T &val) { if (res < val){ res = val; return true; }; return false; }
template<typename T> bool minimize(T &res, const T &val) { if (res > val){ res = val; return true; }; return false; }
using namespace std;
typedef pair<int,int> ii;
typedef pair<int,ii> iii;
typedef vector<int> vi;
/**
* Cho mảng n phần tử tượng trưng cho n bóng đèn (tất cả đều bật) và q truy vấn,
*
* Giả sử ta xuất phát từ vị trí i, ta lần lượt xóa 2 bóng đèn ở 2 bên cho đến khi không còn bóng
* đèn nào:
* Gọi i là thằng gần nhất bên trái còn bật
* j là thằng gần nhất bên phải còn bật
* Nếu không có j thì tắt i.
* không có i thì tắt j.
* Nếu a[i] < a[j] thì tắt thằng j
* a[i] > a[j] thì tắt thằng i
* a[i] = a[j] thì tắt thằng nào cx được --> có nhiều kế hoạch
* Đếm số kế hoạch có thể nếu ta xuất phát từ vị trí i.
* subtask 0: N <= 20, Q <= 20.
* subtask1: N <= 2000, Q <= 2000 (22)
* VỚi 1 truy vấn, ta cần giải trong O(N).
* Giả sử ta giải cho thằng i. thì liệu ta có cần phải bắt đầu lại từ đầu.
*
* Với 1 thằng thì đầu tiên là mình có cái thuật dp[l][r] số lượng kịch bản khi trong đoạn [l, r]
* Mọi thứ trong random quá nhỉ.
* Một thằng bị xóa khi nào.
*
* Nói chung là giải trong O(N^2). gọi dp[l][r] là kết quả của đoạn [l, r] thì kq của
* ta nằm ở dp[l][l]
*
* subtask2: N <= 1e5, k <= 5 (38).
*
* Từ 1 vị trí, thì có 1 số đoạn sẽ là tuyến tính (tức là chỉ có 1 lối đi), ngược
* lại sẽ có những chỗ mà ta phải nhân đôi.
*
* Bây giờ ta cần biết những thông tin gì tại một vị trí. Ta cần biết [l, r] để coi sẽ xóa
* thằng nào tiếp theo. Thực ra thì [l, r] không phải vấn đề nhưng mà khi gặp 2 thằng giống nhau
* thì nó sẽ tách thành 2 bài toán con và có thể gây ra độ phức tạp O(N^2). Tức là những đoạn mà
* mình quan ngại là những đoạn chứa mà 2 thằng 2 bên bằng nhau.
*
* Giả sử ta đang xét đoạn [l, r] mà có a[l] = a[r] thì lúc này nếu tồn tại 1 số a[x] < a[l] và
* khác điểm xuất phát thì ta không cần quan tâm cặp (l, r) này ra sao vì nó sẽ không thể nhân đôi
* được. Tại sao.
* Giả sử có a[x] thì nó sẽ chỉ thể bị xóa bởi 1 số a[y] khác nhỏ hơn nó và a[y] sẽ xóa được cái
* thằng biên giới.
*
* Nếu như có 1 đoạn [l, r] chứa vị trí i thì ta có thể nhảy nhanh đến đoạn đó.
*
* Bây giờ ta cần 1 thuật khởi tạo O(N) mà giải quyết nhanh bài này được.
*
* Mình cần thoát ra khỏi tư duy tuần tự nếu muốn giải nhanh bài này.
*
* Xét vị trí i, ta gọi lb[i] và rb[i] là vị trí biên trái và biên phải của đoạn mà vị trí i
* làm min.
* Nếu i xuất phát từ bên trái
*
* Rời khỏi thuật dp ban đầu
* Nhận xét của ta: Gọi X là vị trí xuất phát, Gọi i và i + 1 là 2 vị trí nằm bên phải vị trí i.
* Nếu Ai < Ai + 1 thì ta chắc chắn là bóng đèn i + 1 sẽ bị tắt ngay sau bóng đèn i. Nên ta
* Tương tự với 1 vị trí bên trái ==> Ta đưa về các vị trí quan trọng sao cho
* A1 < A2 < A3 < X > A4 > A5 > A6 với X là vị trí ta chọn.
*
* subtask3: N <= 1e5, Q <= 1e5. (40)
*/
const int MAXN = 3e5 + 9;
int n, q;
int Query[MAXN], a[MAXN];
void add(int &a, int b){
a += b;
if (a >= MOD) a -= MOD;
}
void sub(int &a, int b){
a -= b;
if (a < 0) a += MOD;
}
int mul(int a, int b){
return (a % MOD) * (b % MOD) % MOD;
}
int fact[MAXN], ifact[MAXN];
int binpow(int a, int b){
int res = 1;
while(b > 0){
if (b&1) res = mul(res, a);
b >>= 1;
a = mul(a, a);
}
return res;
}
void prep(int n){
fact[0] = 1;
for(int i = 1; i <= n; i++){
fact[i] = mul(fact[i - 1], i);
}
ifact[n] = binpow(fact[n], MOD - 2);
for(int i = n; i >= 1; i--){
ifact[i - 1] = mul(ifact[i], i);
}
}
int C(int n, int k){
return mul(fact[n], mul(ifact[k], ifact[n - k]));
}
namespace subtask1{
bool check(){
return n <= 2000 and q <= 2000;
}
int dp[2002][2002];
void solve(){
dp[1][n] = 1;
a[0] = -inf;
a[n + 1] = -inf;
for(int len = n - 1; len >= 1; len--){
for(int i = 1, j = i + len - 1; j <= n; j++, i++){
if (a[i - 1] >= a[j + 1]) add(dp[i][j], dp[i - 1][j]);
if (a[i - 1] <= a[j + 1]) add(dp[i][j], dp[i][j + 1]);
}
}
for(int i = 1; i <= q; i++){
cout << dp[Query[i]][Query[i]] << endl;
}
}
}
namespace subtask2{
bool check(){
return n <= 2e5 and q <= 5;
}
int cnt[200005], cntLeft[200005];
int oneQuery(int p){
vector<int> stk;
memset(cnt, 0, sizeof(cnt));
memset(cntLeft, 0, sizeof(cntLeft));
for(int i = p + 1; i <= n; i++){
if (stk.empty()) stk.pb(i);
else if (a[i] <= a[stk.back()]){
stk.pb(i);
}
}
for(auto x: stk){ cnt[a[x]]++; }
stk.clear();
for(int i = p - 1; i >= 1; i--){
if (stk.empty()) stk.pb(i);
else if (a[i] <= a[stk.back()]){
stk.pb(i);
}
}
for(auto x: stk){
cnt[a[x]]++;
cntLeft[a[x]]++;
}
int ans = 1;
for(int i = 1; i <= 200000; i++){
ans = mul(ans, C(cnt[i], cntLeft[i]));
// if (cnt[i] > 0) printf("%d %d %d %d\n",i, cnt[i], cntLeft[i], C(cnt[i], cntLeft[i]));
}
return ans;
}
void solve(){
prep(200000);
for(int i = 1; i <= q; i++){
cout << oneQuery(Query[i]) << endl;
}
}
}
main()
{
fast;
if (fopen(TASKNAME".inp","r")){
freopen(TASKNAME".inp","r",stdin);
freopen(TASKNAME".out","w",stdout);
}
cin >> n >> q;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
for(int i = 1; i <= q; i++){
cin >> Query[i];
}
if (subtask1::check()) return subtask1::solve(), 0;
if (subtask2::check()) return subtask2::solve(), 0;
}
/**
Warning:
- MLE / TLE?
- Gioi han mang?
- Gia tri max phai luon gan cho -INF
- long long co can thiet khong?
- tran mang.
- code can than hon
**/
Compilation message
zarulje.cpp:197:1: warning: ISO C++ forbids declaration of 'main' with no type [-Wreturn-type]
197 | main()
| ^~~~
zarulje.cpp: In function 'int main()':
zarulje.cpp:201:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
201 | freopen(TASKNAME".inp","r",stdin);
| ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
zarulje.cpp:202:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
202 | freopen(TASKNAME".out","w",stdout);
| ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~
# |
Verdict |
Execution time |
Memory |
Grader output |
1 |
Correct |
1 ms |
3160 KB |
Output is correct |
2 |
Correct |
5 ms |
8284 KB |
Output is correct |
3 |
Correct |
17 ms |
23132 KB |
Output is correct |
4 |
Correct |
16 ms |
23000 KB |
Output is correct |
5 |
Correct |
17 ms |
23192 KB |
Output is correct |
6 |
Correct |
16 ms |
23132 KB |
Output is correct |
# |
Verdict |
Execution time |
Memory |
Grader output |
1 |
Correct |
15 ms |
7932 KB |
Output is correct |
2 |
Correct |
20 ms |
8932 KB |
Output is correct |
3 |
Correct |
18 ms |
8728 KB |
Output is correct |
4 |
Correct |
20 ms |
9052 KB |
Output is correct |
5 |
Correct |
20 ms |
9280 KB |
Output is correct |
6 |
Correct |
21 ms |
9308 KB |
Output is correct |
7 |
Correct |
22 ms |
9552 KB |
Output is correct |
# |
Verdict |
Execution time |
Memory |
Grader output |
1 |
Correct |
1 ms |
3160 KB |
Output is correct |
2 |
Correct |
5 ms |
8284 KB |
Output is correct |
3 |
Correct |
17 ms |
23132 KB |
Output is correct |
4 |
Correct |
16 ms |
23000 KB |
Output is correct |
5 |
Correct |
17 ms |
23192 KB |
Output is correct |
6 |
Correct |
16 ms |
23132 KB |
Output is correct |
7 |
Correct |
15 ms |
7932 KB |
Output is correct |
8 |
Correct |
20 ms |
8932 KB |
Output is correct |
9 |
Correct |
18 ms |
8728 KB |
Output is correct |
10 |
Correct |
20 ms |
9052 KB |
Output is correct |
11 |
Correct |
20 ms |
9280 KB |
Output is correct |
12 |
Correct |
21 ms |
9308 KB |
Output is correct |
13 |
Correct |
22 ms |
9552 KB |
Output is correct |
14 |
Incorrect |
1 ms |
2648 KB |
Output isn't correct |
15 |
Halted |
0 ms |
0 KB |
- |