A
做法一:答案一定是相邻两个的差的最小值。
考虑反证法:如果 $[l,r](l+1 < r)$ 是答案,那么中间一定能取到一段小于等于平均数的。
做法二:二分答案。
首先答案是 $\min_{i,j} \frac{a_i-a_j}{j-i}$,考虑二分最小值,那么相当于对于所有 $j>i$ 要有 $a_i-a_j \geq kj-ki$,也就是 $a_i+k_i \geq a_j+k_j$,判断是否不增就行。
#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;
int n;
LL a[MAXN];
inline bool chk(LL k){
FOR(i,2,n){
if(a[i-1]+k*(i-1) < a[i]+k*i) return 0;
}
return 1;
}
int main(){
scanf("%d",&n);FOR(i,1,n) scanf("%lld",a+i);
LL l = 0,r = 2e9,ans = -1;
while(l <= r){
LL mid = (l + r) >> 1;
if(chk(mid)) ans = mid,l = mid+1;
else r = mid-1;
}
printf("%lld\n",ans);
return 0;
}
B
意思是前 $\frac{n}{2}$ 是排名前 $\frac{n}{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 MAXN = 5e5 + 5;
int a[MAXN],n;
P b[MAXN];
bool vis[MAXN];
int main(){
scanf("%d",&n);
FOR(i,1,n) scanf("%d",a+i),b[i] = MP(a[i],i);
std::sort(b+1,b+n+1);
FOR(i,1,n/2) vis[b[i].se] = 1;
int tp = 0;FOR(i,n/2+1,n) tp += vis[i];
LL ans = 0;
std::vector<int> p1,p2;
FOR(i,1,n/2) if(!vis[i]) p1.pb(i);
FOR(i,n/2+1,n) if(vis[i]) p2.pb(i);
FOR(i,0,tp-1) ans += p2[i]-p1[tp-i-1];
printf("%lld\n",ans);
return 0;
}
C
$lcp$ 问题,可以考虑建 trie 树,两个串的 $lcp$ 就是 $lca$。
然后发现我们在每一个点肯定是尽量匹配子树内的点,所以自下往上贪心即可。注意卡空间,用 map 或者 vector 优化。
#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 = 2e6 + 5;
int n,k;
//std::map<int,int> ch[MAXN];
std::vector<P> ch[MAXN];
//std::vector<int> ch[MAXN];
int dep[MAXN];
char str[100000+5];
int rt = 1,tot = 1;
int sm[MAXN];
inline int find(int v,int y){
for(auto x:ch[v]) if(x.fi == y) return x.se;
return 0;
}
inline void insert(char str[]){
int len = strlen(str+1),v = rt;
FOR(i,1,len){
int o = str[i]-'a';
int t = find(v,o);
if(!t) ch[v].pb(MP(o,++tot)),dep[tot] = dep[v]+1,t = tot;
v = t;
}
sm[v]++;
}
LL ans = 0;
inline void dfs(int v){
for(auto x:ch[v]){
dfs(x.se);
sm[v] += sm[x.se];
}
ans += dep[v]*(sm[v]/k);
sm[v] %= k;
}
int main(){
// freopen("C.in","r",stdin);
// freopen("C.out","w",stdout);
scanf("%d%d",&n,&k);
FOR(i,1,n){
scanf("%s",str+1);insert(str);
}
dfs(1);
printf("%lld\n",ans);
return 0;
}
D
比较有意思,但是数据太水了。
这种东西显然我们有一个单次询问 $O(k^2 \log n)$ 的做法:转化为 $k^2$ 次二维数点。
那么我们考虑对着根号分治一下:设 $T = \sqrt{\sum_i k_i}$,如果 $k \geq T$,这样的询问不会有超过 $T$ 个,暴力即可。
如果 $k < T$,拆成 $k^2$ 个询问,我们来分析下询问次数最大有多少个(我之前一直以为是 $k^2$ 个,才发现不是)。
那么可以使用 $O(T)-O(1)$ 的分块技巧实现单点修改,前缀查询,根号平衡后复杂度是 $O(k\sqrt k)$ 的。
#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 = 2e5 + 5;
int n,m,N,q;
int ans[MAXN];
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 Node{
int y;char opt,d;int id;// 0 = mod
Node(int y=0,char opt=0,char d=0,int id=0) : y(y),opt(opt),d(d),id(id) {}
};
std::vector<Node> G[MAXN];
int l[MAXN],r[MAXN],B;
inline void add(int x1,int x2,int y1,int y2,int d,int id){
if(x1 > x2 || y1 > y2) return;
G[x2].pb(Node(y2,1,1*d,id));
G[x2].pb(Node(y1-1,1,-1*d,id));
G[x1-1].pb(Node(y2,1,-1*d,id));
G[x1-1].pb(Node(y1-1,1,1*d,id));
}
const int MAXM = 500+5;
struct DS{// O(sqrt(n)) 单点修改 O(1) 查询前缀和
int B,n;
int bel[MAXN];
int sm[MAXM][MAXM],pre[MAXM];
inline void build(int xxx){
n = xxx;B = std::sqrt(1.0*n);
FOR(i,1,n) bel[i] = (i-1)/B+1;
}
inline void add(int pos,int x){
// FOR(i,pos,n) pre[i] += x;
// return;
FOR(i,bel[pos]+1,bel[n]) pre[i] += x;
FOR(i,pos-(bel[pos]-1)*B,bel[pos]*B-(bel[pos]-1)*B) sm[bel[pos]][i] += x;
}
inline int query(int x){
// return pre[x];
return pre[bel[x]]+sm[bel[x]][x-(bel[x]-1)*B];
}
}ds;
bool vis[MAXN];
int uu[MAXN],vv[MAXN];
int main(){
// freopen("D.in","r",stdin);
// freopen("D.out","w",stdout);
read(n);read(m);read(q);
FOR(i,1,m){
int u,v;read(u);read(v);
if(u > v) std::swap(u,v);
uu[i] = u;vv[i] = v;
G[u].pb(Node(v,0));
}
B = 300;
FOR(id,1,q){
int k;read(k);
if(k >= B){
FOR(i,1,n) vis[i] = 0;
FOR(i,1,k){
int l,r;read(l);read(r);
FOR(j,l,r) vis[j] = 1;
}
FOR(i,1,m) ans[id] += vis[uu[i]]&vis[vv[i]];
}
else{
FOR(i,1,k) read(l[i]),read(r[i]);
// if(id != 6) continue;
FOR(i,1,k){
FOR(j,i,k){
add(l[i],r[i],l[j],r[j],1,id);
}
}
}
}
ds.build(n);
FOR(i,0,n){
for(auto x:G[i]){
if(x.opt == 0) ds.add(x.y,1);
}
for(auto x:G[i]){
if(x.opt == 1) ans[x.id] += x.d*ds.query(x.y);
}
}
// DEBUG(ans[1]);
FOR(i,1,q) puts(ans[i]?"GG":"SAFE");
return 0;
}