题意:有n个电梯, 建筑有m层。每一个电梯从小到大有一个1-n的编号和出发的时间ai。在除了第一层和第m层都有一个开关,可以使到达这里的电梯停留1ms,问你对于第i个编号的电梯,如果要让它第一个到达m层,需要按多少层的开关。(如果有电梯同时到达,算编号小的先到)
6 20
3 8 12 6 9 9
0
8
-1
4
13
14
思路:观察样例可以知道,编号为i的电梯的作为第一个到达m层的时间=ai - 在前面出发的电梯的时间aj, 且如果j < i,编号小,ans还得额外+1. 那
那么就很简单了,我们只需要去维护一个数据结构,它支持记录当前位置前面的出发的电梯的总个数和总时间即可。
那么直接用树状数组维护一下即可。c1维护前面编号小且aj小的电梯个数。c2维护前面编号小的电梯出发的时间。因为我们将出发时间从小到大排序了,所以对于编号比当前大的后缀的数,直接用总的减去前缀即可。
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define x first
#define y second
#define endl '\n'
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define repn(i,a,n) for (int i = a; i <= n; i ++ )
#define pb push_back
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
#define all(x) (x).begin(),(x).end()
typedef pair<int,int> PII;
ll lowbit(ll x) { return x & (- x); }
ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
const int mod = 1e9+7;
// Binary Index Tree
// Fenwick's Tree
const int N = 500010;
template<class T>
struct BIT {
T c[N];
int size;
void init(int s) {
size = s;
for (int i = 1; i <= s; i++) c[i] = 0;
}
T query(int x) { // 1 ... x
//assert(x <= size);
T s = 0;
for (; x; x -= x & (-x)) {
s += c[x];
}
return s;
}
void modify(int x, T s) { // a[x] += s
assert(x != 0);
for (; x <= size; x += x & (-x)) {
c[x] += s;
}
}
};
BIT<ll> c1, c2;
vector<PII> v;
ll ans[N];
int n, m, a[N];
int main(){
IOS;
cin >> n >> m;
repn(i, 1, n) cin >> a[i], v.pb({a[i], i});
sort(all(v));
c1.init(n);
c2.init(n);
ll s = 0;//s是记录和的
rep(i, 0, n) {
auto [val, pos] = v[i];
ans[pos] += c1.query(pos) * (val + 1) - c2.query(pos);//前缀的
ans[pos] += (i - c1.query(pos)) * val - (s - c2.query(pos));
//第一个ans[pos]是查询前缀中的差值, aj - ai(i < j) = 前面比aj小的个数 * aj - 前面的和, 因为位置问题还要加1
//第二个ans是记录后缀中的差值, 因为插入是一步步从小到大的,比它小的一定出现了,
//用当前的个数 - 前缀的个数就是后缀中的数字个数 * val - (后缀的总和 = s - 前缀总和) s是记录当前未插入之前的和
s += val;
c1.modify(pos, 1);//当前位置++, 出现了1次
c2.modify(pos, val);//当前位置的值是val
}
repn(i, 1, n) {
if(ans[i] > m - 2) ans[i] = -1;
cout << ans[i] << endl;
}
return 0;
}
博弈论:其实我屁都不会
Alice和Bob玩游戏, 定义Mex(s)是s中未出现过的最小自然数
每次Alice和Bob可以向集合中放数,最后MEX(s)是偶数Alice就赢了, 反之Bob赢了。
思路:因为要找最小的没出现过的数,要使一个数成为最终的判定数,小于他的数必须都出现过,
所以我们无论怎么放,都等价于从最小的数开始逐个往大放,我们从0开始遍历。A的最优策略就是放偶数,B的最优策略就是放奇数,我们从0开始遍历每个没在集合中的数,然后让AB进行他们所有的K个回合后,再重新遍历,看哪个数第一个没有出现。模拟
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define x first
#define y second
#define endl '\n'
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define repn(i,a,n) for (int i = a; i <= n; i ++ )
#define pb push_back
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
#define all(x) (x).begin(),(x).end()
typedef pair<int,int> PII;
ll lowbit(ll x) { return x & (- x); }
ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
const int mod = 1e9+7;
const int N = 200010;
void solve() {
int n, k; cin >> n >> k;
map<int, int> mp;
for (int i = 1; i <= n; i ++ ) {
int x; cin >> x;
mp[x] ++;
}
int now = 0;
int opa = k, opb = k;
while(opa || opb) {
while(mp[now]) now ++;//找出不在集合中的第一个元素
if(opb != 0 && now & 1) {
opb --;
mp[now] ++;
}
else if(opa != 0 && now % 2 == 0) {
opa --;
mp[now] ++;
}
now ++;
}
now = 0;
while(mp[now]) now ++;//找出第一个不在集合中的元素
if(now & 1) puts("Bob");
else puts("Alice");
}
int main(){
IOS;
int t;
cin >> t;
while(t -- ){
solve();
}
return 0;
}
有n个人,m个车站,问你有多少种分配方式,保证每个车站必须有人。
思路:他妈的插板法。要把n个人分成m组,因为有n-1个空挡,我们只需要在n-1个空档中插入m-1个板子就行了, 又因为题目规定不同的排列算不同,所以再×排列的个数n!,这就是答案了。
记得算组合数Cn-1(m-1)时用一下费马小定理,将÷改成×。
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define x first
#define y second
#define endl '\n'
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define repn(i,a,n) for (int i = a; i <= n; i ++ )
#define pb push_back
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
#define all(x) (x).begin(),(x).end()
typedef pair<int,int> PII;
#define int long long
ll lowbit(ll x) { return x & (- x); }
ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
const int mod = 998244353;
const int N = 200010;
int f[N];
int qpow(int x) {
int res = 1, b = mod - 2;
while (b) {
if (b & 1)
res = res * x % mod;
x = x * x % mod;
b >>= 1;
}
return res;
}
void init() {
f[0] = 1;
for (int i = 1; i <= N; i ++ ) f[i] = (f[i - 1] * i) % mod;
}
int C(int a, int b) {
if(b == 0) return 1;
return 1ll * f[a] * qpow(f[b]) % mod * qpow(f[a - b]) % mod;//费马小定理
}
void solve() {
int n, m; cin >> n >> m;
cout << f[n] * C(n - 1, m - 1) % mod << endl;
}
signed main(){
IOS;
init();
int t;
cin >> t;
while(t -- ){
solve();
}
return 0;
}
/*
where to find:
就是考虑n个人, 然后分成m组, 且n个人的顺序是可以不一样的
那么我们考虑一下插板法
n个人有n-1个空挡, 只需要插入m-1块板子, 就可以分成m组, 然后因为n个人的顺序不一样,所以×n!
*/