A
贪心删除出现次数最少的数。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 2e5 + 5;
int n,a[MAXN],b[MAXN],k;
int main(){
scanf("%d%d",&n,&k);
FOR(i,1,n) scanf("%d",a+i),b[a[i]]++;
int t = 0;FOR(i,1,n) t += (b[i] != 0);
std::sort(b+1,b+n+1);
int ans = 0;
FOR(i,1,n){
if(t <= k) break;
if(!b[i]) continue;
ans += b[i];t--;
}
printf("%d
",ans);
return 0;
}
B
逆序对期望,根据期望的线性性转成考虑每一对数的期望,由于权值是 (0/1),现在变成了对于一对数 ((x,y)),计算是一对逆序对的概率。
我们设 (x<y),那么一定是先共同经过若干轮都没有被选中,然后在第 (i) 轮选中了 (y) 但没有选中 (x),之后若干轮后选了 (x);我们枚举 (i) 可以得到概率:
注意到对数有 (inom n 2) 个,所以答案是 (inom n 2 p(1-p)frac{1}{1-(1-p)^2})。
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int ha = 998244353;
const int inv2 = 499122177;
inline int qpow(int a,int n=ha-2){
int res = 1;
while(n){
if(n & 1) res = 1ll*res*a%ha;
a = 1ll*a*a%ha;
n >>= 1;
}
return res;
}
int main(){
int n,x,y;scanf("%d%d%d",&n,&x,&y);
int p = 1ll*x*qpow(y)%ha;
int ans = 1ll*n*(n-1)%ha*inv2%ha;
int t = (1+ha-p)%ha;t = 1ll*t*t%ha;
t = (1+ha-t)%ha;t = qpow(t);
ans = 1ll*ans*t%ha;
ans = 1ll*ans*(1+ha-p)%ha;
ans = 1ll*ans*p%ha;
printf("%d
",ans);
return 0;
}
C
提供我考场的思路和标程思路,我的思路应该是正规 NOIP 思路((虽然代码本质是一样的
首先我们考虑有以下恒等式:
所以对于一条路径的 (x^k) 可以理解成有序可重地选出 (k) 条边,所以一个暴力dp的思想就是设 (f_{v,i}) 表示从 (1) 到 (v) 选了 (i) 个边的方案数,每次转移 (O(k)) 枚举当前边被选了 (j) 次,系数是 (inom{k-i}{j}),这样复杂度是 (O(mk^2)) 的。
优化:我们可以考虑每条路径的系数只和选了多少条不同的边有关,假设选了 (m) 条不同的边,系数和大概是这样的一个东西:
这个东西我考试的时候写了 (k^3) dp 出来的,但是可以发现实际上它是将 (k) 个有标号小球放进 (m) 个有标号小球,要求非空的方案数,也就是 (left{^k_m ight}m!)。
所以我们 dp 的时候只需要求出 (f_{v,i}) 到 (v) 点,选了 (i) 条边的方案数,转移直接枚举当前边是否选,(O(mk + k^2))。
题解做法(一点点省选知识):考虑恒等式:
所以我们只需要求出 (f_{v,i}) 表示到 (v) 点,所有路径的 (inom x k) 的和即可,注意到有 (inom n m = inom {n-1}{m} + inom {n-1}{m-1}),转移的时候只要枚举当前边是否选就行了。
对于这种只能从一个点出发的 DAG 游走,只需要初始化起点然后正常拓扑排序即可。
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 1e5 + 5;
const int MAXK = 500 + 5;
const int ha = 998244353;
inline char nc(){
#define SIZE 1000000+3
static char buf[SIZE],*p1 = buf+SIZE,*p2 = buf+SIZE;
if(p1 == p2){
p1 = buf;p2 = buf+fread(buf,1,SIZE,stdin);
if(p1 == p2) return -1;
}
return *p1++;
#undef SIZE
}
template <typename T>
inline void read(T &x){
x = 0;int flag = 0;char ch = nc();
while(!isdigit(ch)){
if(ch == '-') flag = 1;
ch = nc();
}
while(isdigit(ch)){
x = (x<<1) + (x<<3) + (ch^'0');
ch = nc();
}
if(flag) x = -x;
}
struct Edge{
int to,nxt;
}e[MAXN<<1];
bool vis[MAXN];
int head[MAXN],cnt,deg[MAXN];
inline void adde(int u,int v){
e[++cnt] = (Edge){v,head[u]};head[u] = cnt;
deg[v]++;
}
inline void add(int &x,int y){
(x += y);if(x >= ha) x -= ha;
}
int f[MAXN][MAXK];
int n,m,k;
int fac[MAXN],inv[MAXN];
inline int qpow(int a,int n=ha-2){
int res = 1;
while(n){
if(n & 1) res = 1ll*res*a%ha;
a = 1ll*a*a%ha;
n >>= 1;
}
return res;
}
inline void prework(){
fac[0] = 1;FOR(i,1,MAXN-1) fac[i] = 1ll*fac[i-1]*i%ha;
inv[MAXN-1] = qpow(fac[MAXN-1]);
ROF(i,MAXN-2,0) inv[i] = 1ll*inv[i+1]*(i+1)%ha;
}
inline int C(int n,int m){
if(n < 0 || m < 0 || n < m) return 0;
return 1ll*fac[n]*inv[m]%ha*inv[n-m]%ha;
}
int g[MAXK][MAXK];
// i个变量,和为 j
int main(){
prework();
read(n);read(m);read(k);
g[0][0] = 1;
FOR(i,1,k){
FOR(j,i,k){
FOR(x,1,j){
add(g[i][j],1ll*g[i-1][j-x]*C(k-(j-x),x)%ha);
}
}
}
FOR(i,1,m){
int u,v;read(u);read(v);
adde(u,v);
}
std::queue<int> q;
q.push(1);vis[1] = 1;
while(!q.empty()){
int v = q.front();q.pop();
for(int i = head[v];i;i = e[i].nxt){
if(vis[e[i].to]) continue;
q.push(e[i].to);vis[e[i].to] = 1;
}
}
FOR(v,1,n){
if(!vis[v]){
for(int i = head[v];i;i = e[i].nxt){
deg[e[i].to]--;
}
}
}
q.push(1);
f[1][0] = 1;
// assert(deg[1]==0);
FOR(i,1,n) vis[i] = 0;
while(!q.empty()){
int v = q.front();q.pop();vis[v] = 1;
for(int i = head[v];i;i = e[i].nxt){
FOR(x,0,k){
if(!f[v][x]) continue;
FOR(y,0,std::min(1,k-x)){
add(f[e[i].to][x+y],f[v][x]);
}
}
if(!--deg[e[i].to]) q.push(e[i].to);
}
}
FOR(i,1,n){
int ans = 0;
FOR(j,1,k){
add(ans,1ll*f[i][j]*g[j][k]%ha);
}
printf("%d
",ans);
}
return 0;
}
D
曼哈顿距离两维独立考虑,一维上给你若干个点,求一个点满足曼哈顿距离和最小是经典问题:只需要取中位数即可。
那么对于这个题我们肯定要二分中位数 (x),然后变成了计算有多少个交点满足横坐标 (leq x)(求纵坐标的时候只需要求一下反函数即可)。
赛时思路:
按照斜率排序,考虑 (i,j) 两条直线((i < j))的交点横坐标是 (frac{b_i-b_j}{k_j-k_i}),也就是要判断是否 (frac{b_i-b_j}{k_j-k_i} leq x Rightarrow b_i+k_ix leq k_jx+b_j),离散化+树状数组或者归并排序求顺序对都可以。
题解思路:
考虑我们按照 (-infty) 的值排序,观察走到 (x) 的变化:如果有交点就会产生一对逆序对。
所以我们只需要一开始按照 (-infty) 的值编号,每次 check 的时候按照 (x) 的位置的 (y) 值排序,逆序对就是答案。
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define fi first
#define se second
#define db double
#define U unsigned
#define P std::pair<int,int>
#define LL long long
#define pb push_back
#define MP std::make_pair
#define all(x) x.begin(),x.end()
#define CLR(i,a) memset(i,a,sizeof(i))
#define FOR(i,a,b) for(int i = a;i <= b;++i)
#define ROF(i,a,b) for(int i = a;i >= b;--i)
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl
const int MAXN = 40000+5;
const int EPS = 1e-9;
inline int sgn(double x){
if(std::fabs(x) <= EPS) return 0;
if(x < 0) return -1;
return 1;
}
int n;
int aa[MAXN],bb[MAXN],cc[MAXN];
std::pair<double,double> seg[MAXN];
double a[MAXN];
int c[MAXN];
struct BIT{
int tree[MAXN];
#define lowbit(x) ((x)&(-(x)))
inline void add(int pos,int x){
while(pos < MAXN){
tree[pos] += x;
pos += lowbit(pos);
}
}
inline int query(int pos){
int res = 0;
while(pos){
res += tree[pos];
pos -= lowbit(pos);
}
return res;
}
}bit;
inline bool chk(double x){ // <=x 的交点个数是否超过一半
LL ans = 0;
std::sort(seg+1,seg+n+1);
FOR(i,1,n) a[i] = seg[i].se+x*seg[i].fi;
std::vector<double> S;
FOR(i,1,n) S.pb(a[i]);
std::sort(all(S));S.erase(std::unique(all(S)),S.end());
FOR(i,1,n) c[i] = std::lower_bound(all(S),a[i])-S.begin()+1;
CLR(bit.tree,0);
FOR(i,1,n){
ans += bit.query(c[i]);
bit.add(c[i],1);
}
LL lim = 1ll*n*(n-1)/2;
// DEBUG(ans);
return ans >= lim - ans;
}
inline double work(){
std::sort(seg+1,seg+n+1);
double l = -1e9,r = 1e9,ans=0;
FOR(i,1,70){
double mid = (l + r)/2.0;
if(chk(mid)) ans = mid,r = mid;
else l = mid;
}
return ans;
}
int main(){
scanf("%d",&n);
FOR(i,1,n){
scanf("%d%d%d",aa+i,bb+i,cc+i);
seg[i] = MP(-1.0*(1.0*aa[i]/bb[i]),1.0*cc[i]/bb[i]);
}
printf("%.6f ",work());
FOR(i,1,n){
seg[i] = MP(-1.0*(1.0*bb[i]/aa[i]),1.0*cc[i]/aa[i]);
}
// DEBUG(chk(-4905.877264));
// exit(0);
printf("%.6f
",work());
return 0;
}