期望逆序对 | |
密码学 | 模拟 |
染色图 | |
生成树 | |
树与路径 | |
乘法 | 二分 |
圆凸包 | |
最大公约数 | 唯一分解定理 |
k小数查询 | 区间线段树套权值线段树 |
德州扑克 |
B密码学
注意题目是给出所有操作后的结果,所以需要逆着操作即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e3+5;
const int inf = 0x3f3f3f3f;
char s[1005][105];
char t[1005][105];
int n,m;
int xx[1005],yy[1005];
char change(char a,char b){//a是破解后的
int aa = 0,bb = 0,cc = 0;
if(a >= 'a' && a <= 'z')aa = a - 'a';
else aa = a - 'A' + 26;
if(b >= 'a' && b <= 'z')bb = b - 'a';
else bb = b - 'A' + 26;
if(aa >= bb){
cc = aa - bb;
}else{
cc = aa + 52 - bb;
}
aa = aa % 52;
if(cc >= 0 && cc <= 25)return cc + 'a';
else return cc - 26 + 'A';
}
void solve(){
for(int i = m; i >= 1; i--){
char x[105],y[105];
strcpy(x,t[xx[i]]);
strcpy(y,t[yy[i]]);
int l1 = strlen(x);
int l2 = strlen(y);
for(int j = 0; j < l2; j++){
y[j] = change(y[j],x[j%l1]);
}
strcpy(t[yy[i]],y);
}
}
void print(){
for(int i = 1; i <= n; i++){
cout << t[i] << endl;
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= m; i++){
scanf("%d%d",&xx[i],&yy[i]);
}
for(int i = 1; i <= n; i++){
cin >> s[i];
strcpy(t[i],s[i]);
}
solve();
print();
return 0;
}
F乘法
二分第k小的数字,mid 是一个数字,然后在数组b中进行遍历,看看这个数字在所有数字的排名情况
求出mid在一个常数*a[j]的所有情况的排名很简单,对于常数是0的情况,常数>0的情况,常数<0的情况,二分即可。
很妙的想法
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
ll a[maxn],b[maxn];
ll n,m,k;
bool check(ll mid){
ll cnt = 0;
for(int i = 1; i <= m; i++){
if(b[i] == 0)cnt += mid<0?n:0;
if(b[i] < 0)cnt += lower_bound(a + 1, a + n + 1, ceil((double)mid / b[i])) - (a + 1);
if(b[i] > 0)cnt += n - ((upper_bound(a + 1, a + n + 1, floor((double)mid / b[i]))) - (a + 1));
// cout << cnt << endl;
}
return cnt <= k;
}
int main(){
cin >> n >> m >> k;
k--;
for(int i = 1; i <= n; i++){
scanf("%lld",&a[i]);
}
for(int i = 1; i <= m; i++){
scanf("%lld",&b[i]);
}
sort(a + 1,a + n + 1);
sort(b + 1,b + m + 1);
ll l = -1e13,r = 1e13;
while(l + 1 < r){
ll mid = (l + r) >> 1;
// cout << l << " " << mid << " " << r << endl;
if(check(mid))r = mid;
else l = mid;
}
cout << r << endl;
return 0;
}
H最小公约数
只需要对于gcd(i,y)对于i是[1,n]中除了k以外都不满足gcd(i,y) = gcd(k,y)
也就是gcd(y,k)的值是唯一的
而y只能是y的倍数才行,否则,对于k是质数的情况,gcd(k,y)会是1
那么只需要令ans = k,遍历[1,n]中k的倍数即可,如果说i是质数,那么乘上ans,因为根据唯一分解定理,所有数可以由质数乘积表示,要求出最小y,那么只需要质数即可,而ans初始值为k,因为y必须是k的倍数
import math
def is_prime(n):
if n == 1:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False;
return True
t = int(input())
for tt in range(1, t + 1):
n, k = map(int, input().split())
ans = k
i = 2
while i * k <= n:
if is_prime(i):
ans *= i
i = i + 1
print(ans)
K小数查询
n,m的范围是\(8*10^{4}\)
一种方法是可以暴力O(nm)
一种方法是主席树
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 5;
int n,m;
int a[maxn];
int t[maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)scanf("%d",&a[i]);
while(m--){
int c,l,r,k;
scanf("%d%d%d%d",&c,&l,&r,&k);
if(c == 1){
for(int i = l; i <= r; i++){
a[i] = min(a[i],k);
}
}else{
int sum = 0,last = 0;
memset(t,0,sizeof(t));
for(int i = l; i <= r; i++){
t[a[i]]++;
}
for(int i = 1; i <= n; i++){
last = sum;
sum += t[i];
if(last < sum && sum >= k){
printf("%d\n",i);
break;
}
}
}
}
return 0;
}