咕了整整一天才来写 2333
这次邓老师没来 /kk,不过打的还算不错,可惜还是有一题没调出来,不然就是 11 题队了 /ll
题意好像有点奇怪,不过 djq 还是一眼看对了,拿了这题一血。
看对题就没啥好说的了,裸签到题,把点按权值从大到小排序,不断加入点,用并查集维护当前连通块个数,乘一下相邻两个权值的差加起来即可。复杂度 O ( n log n ) O(n \log n) O(nlogn)。
code by djq
#include
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int MOD = 998244353;
int n, m, b[100005];
PII ver[100005];
int rt[100005];
int root(int x)
{
if(rt[x] == x) return x;
return rt[x] = root(rt[x]);
}
bool unite(int u, int v)
{
u = root(u); v = root(v);
if(u == v) return false;
rt[v] = u;
return true;
}
vector<int> G[100005];
void solve()
{
scanf("%d%d", &n, &m);
rep1(i, n) scanf("%d", &b[i]);
rep1(i, n) ver[i] = MP(b[i], i);
sort(ver + 1, ver + 1 + n);
reverse(ver + 1, ver + 1 + n);
ver[n + 1].first = 0;
rep1(i, n) G[i].clear();
rep(i, m) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
rep1(i, n) rt[i] = i;
int cnt = 0;
LL ans = 0;
rep1(i, n) {
cnt ++;
int v = ver[i].second;
rep(j, G[v].size()) {
int u = G[v][j];
if(b[u] >= b[v] && unite(u, v)) cnt --;
}
ans += 1LL * (ver[i].first - ver[i + 1].first) * cnt;
}
printf("%lld\n", ans);
}
int main()
{
int T;
scanf("%d", &T);
while(T --) solve();
return 0;
}
其实应该也算个比较套路的题?
考虑拆掉绝对值之后每个点的贡献是 -2,0,2,如果在首尾的话比较特别,是 -1 或 1.
由于 a i a_i ai 互不相同,大概按照十三练里波浪那题 dp 就差不多了。
f ( i , j , 0 / 1 / 2 , k ) f(i,j,0/1/2,k) f(i,j,0/1/2,k) 表示当前从小到大加到了第 i i i 个数,有 j j j 个连续段,其中首尾放了 0/1/2 个数时的第 k k k 大。
转移的话就枚举当前的几种情况:产生新的段;在原来段的开头或结尾加入一个(即不改变段数);合并两段。以及还要考虑当前数字是否放在开头/结尾。
合并前 k k k 大,大概写个类似归并的东西就可以了。复杂度 O ( n 2 k ) O(n^2k) O(n2k)。
(转移还是稍微有些复杂的)
#include
typedef long long LL;
using namespace std;
const int MAXN = 605, MOD = 1e9 + 7;
struct Data { int v, c; } f[2][MAXN][3][25];
int T, n, K, aa[MAXN], len[2][MAXN][3];
void calc(Data *a, const Data *b, int &na, int nb, int val, LL coef) {
if (nb == 0) return;
if (na == 0) {
na = nb;
for (int i = 1; i <= na; i++)
a[i] = Data { b[i].v + val, b[i].c * coef % MOD };
return;
}
static Data d[25];
int i = 1, j = 1, c = 0;
while (c < K && (i <= na || j <= nb)) {
if (j > nb || (i <= na && a[i].v > b[j].v + val))
d[++c] = a[i], ++i;
else if (i > na || (j <= nb && b[j].v + val > a[i].v))
d[++c] = Data { b[j].v + val, b[j].c * coef % MOD }, ++j;
else d[++c] = Data { a[i].v, (a[i].c + b[j].c * coef) % MOD }, ++i, ++j;
}
for (int i = 1; i <= c; i++) {
a[i] = d[i];
if (i < c && d[i].v == d[i + 1].v) {
puts("fuck");
}
}
na = c;
}
int main() {
for (scanf("%d", &T); T--;) {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; i++)
scanf("%d", aa + i);
sort(aa + 1, aa + 1 + n);
memset(len, 0, sizeof(len));
f[0][0][0][1] = Data { 0, 1 };
len[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
int a = i & 1, b = !a;
memset(len[a], 0, sizeof(len[a]));
for (int j = 1; j <= i; j++) {
calc(f[a][j][0], f[b][j - 1][0], len[a][j][0], len[b][j - 1][0], -2 * aa[i], j);
calc(f[a][j][0], f[b][j][0], len[a][j][0], len[b][j][0], 0, 2 * j);
calc(f[a][j][0], f[b][j + 1][0], len[a][j][0], len[b][j + 1][0], 2 * aa[i], j);
calc(f[a][j][1], f[b][j - 1][1], len[a][j][1], len[b][j - 1][1], -2 * aa[i], j - 1);
calc(f[a][j][1], f[b][j][1], len[a][j][1], len[b][j][1], 0, 2 * j - 1);
calc(f[a][j][1], f[b][j + 1][1], len[a][j][1], len[b][j + 1][1], 2 * aa[i], j);
calc(f[a][j][1], f[b][j - 1][0], len[a][j][1], len[b][j - 1][0], -aa[i], 2);
calc(f[a][j][1], f[b][j][0], len[a][j][1], len[b][j][0], aa[i], 2);
calc(f[a][j][2], f[b][j - 1][2], len[a][j][2], len[b][j - 1][2], -2 * aa[i], j - 2);
calc(f[a][j][2], f[b][j][2], len[a][j][2], len[b][j][2], 0, 2 * j - 2);
calc(f[a][j][2], f[b][j + 1][2], len[a][j][2], len[b][j + 1][2], 2 * aa[i], j);
calc(f[a][j][2], f[b][j - 1][1], len[a][j][2], len[b][j - 1][1], -aa[i], 1);
calc(f[a][j][2], f[b][j][1], len[a][j][2], len[b][j][1], aa[i], 1);
}
}
for (int i = 1; i <= K; i++) {
const Data &d = f[n & 1][1][2][i];
if (d.c) printf("%d %d\n", d.v, d.c);
else puts("-1");
}
}
return 0;
}
这场比赛里最神的一道题了吧,好像很多人都觉得 q n log n q\sqrt{n \log n} qnlogn 能过然而被卡了 2333.
神仙随机化题,考虑随机 k k k 个 [ 0 , 1 ] [0,1] [0,1] 中的实数,最小值的期望是 1 k + 1 \frac{1}{k+1} k+11。于是考虑给每种颜色随机一个权值,然后求链上权值最小值就可以近似的得到颜色个数。我们多随机几次,取这些最小值的平均值进行比较即可。
于是可以直接树剖维护(倍增空间大预处理复杂度大过不了 qwq),也可以全局平衡二叉树维护(不过好像这题不是查询到根的链,可能难写一些)。
懒得写了,直接贴个题解代码吧。
#include
#include
#pragma GCC optimize(2)
using namespace std;
typedef unsigned int U;
typedef long long ll;
const int N=500010,K=30;
const U inf=~0U;
int Case,n,m,i,j,last,op,x,y,A,B,C,D,col[N],g[N],v[N<<1],nxt[N<<1],ed;
int f[N],d[N],size[N],son[N],top[N],loc[N],q[N],dfn;
U w[N][K],val[1111111][K],tmp[K];
U SX=335634763,SY=873658265,SZ=192849106,SW=746126501;
inline U xorshift128(){
U t=SX^(SX<<11);
SX=SY;
SY=SZ;
SZ=SW;
return SW=SW^(SW>>19)^t^(t>>8);
}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs1(int x,int y){
f[x]=y,d[x]=d[y]+1,size[x]=1;
for(int i=g[x];i;i=nxt[i]){
int u=v[i];
if(u==y)continue;
dfs1(u,x);
size[x]+=size[u];
if(size[u]>size[son[x]])son[x]=u;
}
}
void dfs2(int x,int y){
q[loc[x]=++dfn]=x;
top[x]=y;
if(son[x])dfs2(son[x],y);
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]&&v[i]!=son[x])dfs2(v[i],v[i]);
}
inline void up(int x){for(int i=0;i<K;i++)val[x][i]=min(val[x<<1][i],val[x<<1|1][i]);}
void build(int x,int a,int b){
if(a==b){
int o=col[q[a]];
for(int i=0;i<K;i++)val[x][i]=w[o][i];
return;
}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
up(x);
}
void change(int x,int a,int b,int c,int p){
if(a==b){
for(int i=0;i<K;i++)val[x][i]=w[p][i];
return;
}
int mid=(a+b)>>1;
if(c<=mid)change(x<<1,a,mid,c,p);else change(x<<1|1,mid+1,b,c,p);
up(x);
}
inline void umin(U&a,U b){a>b?(a=b):0;}
void ask(int x,int a,int b,int c,int d){
if(c<=a&&b<=d){
for(int i=0;i<K;i++)umin(tmp[i],val[x][i]);
return;
}
int mid=(a+b)>>1;
if(c<=mid)ask(x<<1,a,mid,c,d);
if(d>mid)ask(x<<1|1,mid+1,b,c,d);
}
inline ll estimate(int x,int y){
for(int i=0;i<K;i++)tmp[i]=inf;
for(;top[x]!=top[y];x=f[top[x]]){
if(d[top[x]]<d[top[y]])swap(x,y);
ask(1,1,n,loc[top[x]],loc[x]);
}
if(d[x]<d[y])swap(x,y);
ask(1,1,n,loc[y],loc[x]);
ll ret=0;
for(int i=0;i<K;i++)ret+=tmp[i];
return ret;
}
int main(){
for(i=1;i<N;i++)for(j=0;j<K;j++)w[i][j]=xorshift128();
scanf("%d",&Case);
while(Case--){
scanf("%d%d",&n,&m);
for(ed=dfn=last=i=0;i<=n;i++)g[i]=f[i]=d[i]=size[i]=son[i]=0;
for(i=1;i<=n;i++)scanf("%d",&col[i]);
for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--){
scanf("%d%d%d",&op,&A,&B);
A^=last,B^=last;
if(op==1)change(1,1,n,loc[A],B);
else{
scanf("%d%d",&C,&D);
C^=last,D^=last;
ll E=estimate(A,B),F=estimate(C,D);
if(E<F){
puts("Yes");
last++;
}else puts("No");
}
}
}
}
本来以为 O ( n 3 ) O(n^3) O(n3) 能过的,后来为了求稳还是写了发主席树……
考虑当前限制了 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 x1,y1,x2,y2 这个矩形,那么最终的路径要么经过 ( x 1 − 1 , y 2 + 1 ) (x_1-1,y2+1) (x1−1,y2+1) 这个格子或其上面的某个鸽子,要么经过 ( x 2 + 1 , y 1 − 1 ) (x2+1,y1-1) (x2+1,y1−1) 或其左边的某个格子。
注意到权值定义为 ( n 2 ) a i , j (n^2)^{a_{i,j}} (n2)ai,j,也就是说能选大的就尽量选大的。我们考虑 dp, f ( i , j ) f(i,j) f(i,j) 表示 ( 1 , 1 ) (1,1) (1,1) 到 ( i , j ) (i,j) (i,j) 能得到的最大值, g ( i , j ) g(i,j) g(i,j) 表示 ( i , j ) (i,j) (i,j) 到 ( n , n ) (n,n) (n,n) 的最大值,于是现在的问题就落到了比较上。
我们用可持久化线段树维护每种数字的出现次数,比较大小的话就直接线段树二分,每个节点维护下面次数的 hash 值和答案(刚好答案也可以作为第二种 hash)。
接下来要算一下对于 ( x , y ) (x,y) (x,y),强制经过其上方的格子能获得的最大值。首先要么是 ( x − 1 , y ) (x-1,y) (x−1,y) 的答案,要么是强制经过 ( x , y ) (x,y) (x,y) 的答案。这个时候我们发现需要比较两个主席树加起来,和另外两个主席树加起来的大小(因为要把 f ( x , y ) + g ( x , y ) f(x,y)+g(x,y) f(x,y)+g(x,y) 作为 ( x , y ) (x,y) (x,y) 的权值)。hash 只要是正常的字符串 hash,就可以直接加,因此还是可以线段树二分的。复杂度 O ( n 2 log n + q log n ) O(n^2 \log n + q \log n) O(n2logn+qlogn)。
自己写的代码找不到了,下面是 code by djq。
#include
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int D0 = 243, D1;
int MOD0 = 998244353, MOD1 = 1e9 + 7;
int PW0[200005], PW1[200005];
void init_pw()
{
PW0[0] = PW1[0] = 1;
rep1(i, 200000) {
PW0[i] = 1LL * PW0[i - 1] * D0 % MOD0;
PW1[i] = 1LL * PW1[i - 1] * D1 % MOD1;
}
}
namespace segt
{
int cnt[8000005], hs0[8000005], hs1[8000005], lson[8000005], rson[8000005], tot;
void init()
{
memset(hs0, 0, sizeof(hs0));
memset(hs1, 0, sizeof(hs1));
memset(lson, -1, sizeof(lson));
memset(rson, -1, sizeof(rson));
tot = 0;
}
void pushup(int cur)
{
hs0[cur] = ((lson[cur] == -1 ? 0 : hs0[lson[cur]]) + (rson[cur] == -1 ? 0 : hs0[rson[cur]])) % MOD0;
hs1[cur] = ((lson[cur] == -1 ? 0 : hs1[lson[cur]]) + (rson[cur] == -1 ? 0 : hs1[rson[cur]])) % MOD1;
}
int modify(int cur, int dat, int cl, int cr)
{
int ret = tot ++;
if(cl == cr) {
cnt[ret] = (cur == -1 ? 0 : cnt[cur]) + 1;
hs0[ret] = 1LL * PW0[dat] * cnt[ret] % MOD0;
hs1[ret] = 1LL * PW1[dat] * cnt[ret] % MOD1;
return ret;
}
int mid = cl + cr >> 1;
if(dat <= mid) {
lson[ret] = modify(cur == -1 ? -1 : lson[cur], dat, cl, mid);
rson[ret] = cur == -1 ? -1 : rson[cur];
} else {
lson[ret] = cur == -1 ? -1 : lson[cur];
rson[ret] = modify(cur == -1 ? -1 : rson[cur], dat, mid + 1, cr);
}
pushup(ret);
return ret;
}
int comp(int a, int b, int cl, int cr)
{
if(a == -1 && b == -1) return 0;
if(a == -1) return 1;
if(b == -1) return -1;
if(hs0[a] == hs0[b] && hs1[a] == hs1[b]) return 0;
if(cl == cr) return cnt[a] < cnt[b] ? 1 : -1;
int mid = cl + cr >> 1;
int ret = comp(rson[a], rson[b], mid + 1, cr);
if(ret == 0) ret = comp(lson[a], lson[b], cl, mid);
return ret;
}
int comp2(int a0, int a1, int b0, int b1, int cl, int cr)
{
int ah0 = ((a0 == -1 ? 0 : hs0[a0]) + (a1 == -1 ? 0 : hs0[a1])) % MOD0;
int ah1 = ((a0 == -1 ? 0 : hs1[a0]) + (a1 == -1 ? 0 : hs1[a1])) % MOD1;
int bh0 = ((b0 == -1 ? 0 : hs0[b0]) + (b1 == -1 ? 0 : hs0[b1])) % MOD0;
int bh1 = ((b0 == -1 ? 0 : hs1[b0]) + (b1 == -1 ? 0 : hs1[b1])) % MOD1;
if(ah0 == bh0 && ah1 == bh1) return 0;
if(cl == cr) return (a0 == -1 ? 0 : cnt[a0]) + (a1 == -1 ? 0 : cnt[a1]) <
(b0 == -1 ? 0 : cnt[b0]) + (b1 == -1 ? 0 : cnt[b1]) ? 1 : -1;
int mid = cl + cr >> 1;
int ret = comp2(a0 == -1 ? -1 : rson[a0], a1 == -1 ? -1 : rson[a1],
b0 == -1 ? -1 : rson[b0], b1 == -1 ? -1 : rson[b1], mid + 1, cr);
if(ret == 0) ret = comp2(a0 == -1 ? -1 : lson[a0], a1 == -1 ? -1 : lson[a1],
b0 == -1 ? -1 : lson[b0], b1 == -1 ? -1 : lson[b1], cl, mid);
return ret;
}
}
int n, q, a[405][405];
int dp0[405][405], f1[405][405], dp1[405][405], lpos[405][405], upos[405][405];
void solve()
{
scanf("%d%d", &n, &q);
rep1(i, n) rep1(j, n) scanf("%d", &a[i][j]);
D1 = n * n;
init_pw();
segt::init();
memset(dp0, -1, sizeof(dp0));
memset(f1, -1, sizeof(f1));
memset(dp1, -1, sizeof(dp1));
memset(lpos, 0, sizeof(lpos));
memset(upos, 0, sizeof(upos));
rep1(i, n) rep1(j, n) {
dp0[i][j] = segt::comp(dp0[i - 1][j], dp0[i][j - 1], 0, 262143) == -1 ? dp0[i - 1][j] : dp0[i][j - 1];
dp0[i][j] = segt::modify(dp0[i][j], a[i][j], 0, 262143);
}
for(int i = n; i >= 1; i --) for(int j = n; j >= 1; j --) {
f1[i][j] = segt::comp(dp1[i + 1][j], dp1[i][j + 1], 0, 262143) == -1 ? dp1[i + 1][j] : dp1[i][j + 1];
dp1[i][j] = segt::modify(f1[i][j], a[i][j], 0, 262143);
}
rep1(i, n) rep1(j, n) {
int lj = lpos[i][j - 1];
lpos[i][j] = segt::comp2(dp0[i][lj], f1[i][lj], dp0[i][j], f1[i][j], 0, 262143) == -1 ? lj : j;
}
rep1(i, n) rep1(j, n) {
int ui = upos[i - 1][j];
upos[i][j] = segt::comp2(dp0[ui][j], f1[ui][j], dp0[i][j], f1[i][j], 0, 262143) == -1 ? ui : i;
}
rep(i, q) {
int lx, rx, ly, ry;
scanf("%d%d%d%d", &lx, &rx, &ly, &ry);
int i0 = rx + 1, j0 = ly - 1, i1 = lx - 1, j1 = ry + 1;
j0 = lpos[i0][j0];
i1 = upos[i1][j1];
int ret = segt::comp2(dp0[i0][j0], f1[i0][j0], dp0[i1][j1], f1[i1][j1], 0, 262143) == -1 ?
(segt::hs1[dp0[i0][j0]] + segt::hs1[f1[i0][j0]]) % MOD1 : (segt::hs1[dp0[i1][j1]] + segt::hs1[f1[i1][j1]]) % MOD1;
printf("%d\n", ret);
}
}
int main()
{
int T;
scanf("%d", &T);
while(T --) solve();
return 0;
}
这个题还是比较 easy 的,考虑对于一个工人,找到他最小值的所在位置扔到一个 vector 里(为了不出错把最小值左右两个整数都加入)。
接下来考虑对于 vector 里的每个元素,找到第一个没有在 set 里出现过的大于它的数字 k k k,把 k k k 扔到 set 里。
同理的,对于 vector 里的每个元素,找到第一个没有在 set 里出现过的小于它的数字 k k k,把 k k k 扔到 set 里。
然后对于 set 里的元素,和工人两两连边,跑 n n n 遍费用流即可。不难发现最优解肯定能够被包含在上面的构造方法中,因此点数 O ( n ) O(n) O(n),边数 O ( n 2 ) O(n^2) O(n2),肯定能过。
code by djq
#include
#define rep(i, n) for(int i = 0; i < (int)(n); i ++)
#define rep1(i, n) for(int i = 1; i <= (int)(n); i ++)
#define MP make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const LL INF = 1.3e17;
namespace flow
{
const int MAXN = 400;
struct edge
{
int to, cap;
LL cost;
};
int n, m;
edge ed[30005];
vector<int> G[405];
void clear()
{
m = 0;
rep(i, n) G[i].clear();
}
void add_edge(int u, int v, int fl, LL co)
{
ed[m].to = v; ed[m].cap = fl; ed[m].cost = co; G[u].push_back(m); m ++;
ed[m].to = u; ed[m].cap = 0; ed[m].cost = -co; G[v].push_back(m); m ++;
}
LL h[405];
LL pat[405];
int prev[405];
bool vis[405];
bool dijk(int s, int t)
{
rep(i, n) {
pat[i] = INF;
vis[i] = false;
}
pat[s] = 0;
priority_queue<pair<LL, int> > que;
que.push(MP(0, s));
while(!que.empty()) {
int cur = que.top().second;
que.pop();
if(vis[cur]) continue;
vis[cur] = true;
rep(i, G[cur].size()) {
int ce = G[cur][i];
if(ed[ce].cap == 0) continue;
if(pat[ed[ce].to] > pat[cur] + ed[ce].cost) {
pat[ed[ce].to] = pat[cur] + ed[ce].cost;
prev[ed[ce].to] = ce;
que.push(MP(-pat[ed[ce].to], ed[ce].to));
}
}
}
return pat[t] != INF;
}
void mcf(int s, int t, LL* ret)
{
rep(i, n) h[i] = 0;
LL ans = 0;
while(dijk(s, t)) {
//in this occasion flow must be 1
int cur = t, cf = 1;
while(cur != s) {
ed[prev[cur]].cap -= cf;
ed[prev[cur] ^ 1].cap += cf;
cur = ed[prev[cur] ^ 1].to;
}
rep(i, m) ed[i].cost += pat[ed[i ^ 1].to] - pat[ed[i].to];
rep(i, n) h[i] += pat[i];
ans += h[t] * cf;
*(++ ret) = ans;
}
}
}
int n, m;
LL a[55], b[55], c[55];
set<LL> S;
vector<LL> hv, nhv;
LL ans[55];
void solve()
{
scanf("%d%d", &n, &m);
rep(i, n) scanf("%lld%lld%lld", &a[i], &b[i], &c[i]);
hv.clear();
nhv.clear();
rep(i, n) {
if(b[i] > 0) hv.push_back(1);
else {
LL pt = max(min((-b[i]) / 2 / a[i], m - 1LL), 1LL);
if(pt >= 1 && pt <= m) hv.push_back(pt);
if(pt + 1 >= 1 && pt + 1 <= m) hv.push_back(pt + 1);
}
}
S.clear();
rep(i, hv.size()) {
int cur = hv[i];
while(S.find(cur) != S.end()) cur ++;
if(cur < 1 || cur > m) continue;
nhv.push_back(cur);
S.insert(cur);
}
S.clear();
rep(i, hv.size()) {
int cur = hv[i];
while(S.find(cur) != S.end()) cur --;
if(cur < 1 || cur > m) continue;
nhv.push_back(cur);
S.insert(cur);
}
sort(nhv.begin(), nhv.end());
nhv.resize(unique(nhv.begin(), nhv.end()) - nhv.begin());
flow::n = n + nhv.size() + 2;
flow::clear();
rep(i, n) flow::add_edge(flow::n - 2, i, 1, 0);
rep(i, nhv.size()) flow::add_edge(i + n, flow::n - 1, 1, 0);
rep(i, n) rep(j, nhv.size())
flow::add_edge(i, j + n, 1, a[i] * nhv[j] * nhv[j] + b[i] * nhv[j] + c[i]);
flow::mcf(flow::n - 2, flow::n - 1, ans);
rep1(i, n) printf("%lld%c", ans[i], " \n"[i == n]);
}
int main()
{
int T;
scanf("%d", &T);
while(T --) solve();
return 0;
}
刚开始以为是那个 PA 原题,吓懵了……仔细一看发现原来只需要判断就行。那就是签到题了,随便取几个质数膜一下判相不相等即可。复杂度 O ( n ) O(n) O(n)。
#include
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 2000005, B = 5;
const int MOD[B] = { 1000000007, 1000000009, 1000000021, 1000000033, 1000000087 };
int fib[MAXN][5], arr[B], brr[B], crr[B], T, na, nb, nc;
bool cc[MAXN];
void init() {
int n = 2e6 + 3;
for (int i = 0; i < B; i++)
fib[0][i] = 1, fib[1][i] = 1;
for (int i = 2; i <= n; i++)
for (int j = 0; j < B; j++)
fib[i][j] = (fib[i - 1][j] + fib[i - 2][j]) % MOD[j];
}
int main() {
init();
for (scanf("%d", &T); T--;) {
scanf("%d", &na);
memset(arr, 0, sizeof(arr));
memset(brr, 0, sizeof(brr));
memset(crr, 0, sizeof(crr));
for (int i = 1; i <= na; i++) {
int t; scanf("%d", &t);
if (t) for (int j = 0; j < B; j++)
arr[j] = (arr[j] + fib[i][j]) % MOD[j];
}
scanf("%d", &nb);
for (int i = 1; i <= nb; i++) {
int t; scanf("%d", &t);
if (t) for (int j = 0; j < B; j++)
brr[j] = (brr[j] + fib[i][j]) % MOD[j];
}
scanf("%d", &nc);
for (int i = 1; i <= nc; i++) {
int t; scanf("%d", &t);
cc[i] = t;
if (t) for (int j = 0; j < B; j++)
crr[j] = (crr[j] + fib[i][j]) % MOD[j];
}
for (int i = 0; i < B; i++)
arr[i] = (LL)arr[i] * brr[i] % MOD[i];
for (int i = 1; i <= nc; i++) if (!cc[i]) {
if (cc[i - 1] || cc[i + 1]) continue;
bool flag = true;
for (int j = 0; j < B; j++)
if ((crr[j] + fib[i][j]) % MOD[j] != arr[j])
{ flag = false; break; }
if (flag) {
printf("%d\n", i);
break;
}
}
}
return 0;
}
要求直径最小,可以二分答案,然后记 f ( i , j ) f(i,j) f(i,j) 表示 i i i 的子树,有 j j j 条边选了权值 a a a 时 i i i 子树的深度最小是多少。转移的话枚举当前儿子选了多少个 a a a,如果当前儿子和之前的儿子形成的路径已经 > m i d >mid >mid 了就不能转移。最后看有没有合法方案即可。复杂度 O ( n k log 1 0 14 ) O(nk \log 10^{14}) O(nklog1014)。
code by lqs
#include
using namespace std;
int test,n,k,x,y,a,b;
long long l,r,mid,dp[22222][22],f[22],gg[22],nf[22];
struct edge
{
int to,a,b;
};
vector<edge> g[22222];
void dfs(int i,int fa)
{
for (int j=0;j<g[i].size();j++)
{
int to=g[i][j].to;
if (to==fa) continue;
dfs(to,i);
}
for (int j=0;j<=k;j++) f[j]=1e18;
f[0]=0;
for (int j=0;j<g[i].size();j++)
{
int to=g[i][j].to;
if (to==fa) continue;
gg[0]=dp[to][0]+g[i][j].b;
for (int h=1;h<=k;h++)
{
gg[h]=min(dp[to][h]+g[i][j].b,dp[to][h-1]+g[i][j].a);
}
for (int h=0;h<=k;h++) nf[h]=1e18;
for (int h=0;h<=k;h++)
{
for (int p=0;p+h<=k;p++)
{
if (f[h]+gg[p]<=mid) nf[h+p]=min(nf[h+p],max(f[h],gg[p]));
}
}
for (int h=0;h<=k;h++) f[h]=nf[h];
}
for (int j=0;j<=k;j++) dp[i][j]=f[j];
}
bool check()
{
dfs(1,0);
return (dp[1][k]<=mid);
}
int main()
{
scanf("%d",&test);
while(test--)
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++) g[i].clear();
for (int i=1;i<n;i++)
{
scanf("%d%d%d%d",&x,&y,&a,&b);
g[x].push_back((edge){y,a,b});
g[y].push_back((edge){x,a,b});
}
l=0;r=1e14;
while(l<=r)
{
mid=(l+r)>>1;
if (check()) r=mid-1;
else l=mid+1;
}
printf("%lld\n",l);
}
return 0;
}
假设没有加入删除操作,我们考虑把这些函数按照 a i a_i ai 排序,接下来从左向右扫维护一个栈,假设当前扫到了 i i i,如果 i i i 和 s t a c k [ t o p ] stack[top] stack[top] 的交点在 s t a c k [ t o p ] stack[top] stack[top] 和 s t a c k [ t o p − 1 ] stack[top-1] stack[top−1] 的交点的左边,那么 s t a c k [ t o p ] stack[top] stack[top] 就可以被删掉了。
查询的话直接二分在哪个函数上即可。
现在有了插入删除操作,我们直接线段树分治。对于每个线段树节点都像上面那样处理;对于一个询问,查询它在线段树上所有祖先的答案取最小值即可。
复杂度 O ( n log 2 n ) O(n \log^2 n) O(nlog2n)。
#include
typedef long long LL;
using namespace std;
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 300005;
struct Func {
int a; LL b;
LL calc(int x) const {
LL t = x - a;
return t * t * t * t + b;
}
} fun[MAXN];
int inter(const Func &a, const Func &b) {
int l = 0, r = 5e4 + 5;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (a.calc(mid) < b.calc(mid)) l = mid;
else r = mid;
}
return l;
}
int tn, T, n, m, beg[MAXN], ed[MAXN], id[MAXN];
struct Seg { Func f; int x; };
vector<Seg> tr[MAXN];
struct Qry { int t, p; };
vector<Qry> qry;
void update(int a, int b, int d, int l = 1, int r = tn, int k = 1) {
if (a > r || b < l) return;
if (a <= l && b >= r) {
while (!tr[k].empty() && (tr[k].back().f.a == fun[d].a ||
tr[k].back().x >= inter(tr[k].back().f, fun[d])))
tr[k].pop_back();
if (tr[k].empty()) tr[k].push_back(Seg { fun[d], 0 });
else tr[k].push_back(Seg { fun[d], inter(tr[k].back().f, fun[d]) });
return;
}
int mid = (l + r) >> 1;
update(a, b, d, l, mid, k << 1);
update(a, b, d, mid + 1, r, k << 1 | 1);
}
LL ask(int k, int x) {
LL ans = 9e18;
for (k += tn - 1; k > 0; k >>= 1) {
int l = 0, r = tr[k].size();
if (!r) continue;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (tr[k][mid].x < x) l = mid;
else r = mid;
}
chkmin(ans, tr[k][l].f.calc(x));
}
return ans;
}
int main() {
for (scanf("%d", &T); T--;) {
scanf("%d%d", &n, &m);
int tim = 1;
for (int i = 1; i <= n; i++)
scanf("%d%lld", &fun[i].a, &fun[i].b);
for (int i = 1; i <= m; i++) {
int o, a; LL b; scanf("%d%d", &o, &a);
if (o == 1) {
scanf("%lld", &b);
fun[++n] = Func { a, b };
beg[n] = ++tim;
} else if (o == 2) {
ed[a] = tim++;
} else qry.push_back(Qry { tim, a });
}
for (int i = 1; i <= n; i++) id[i] = i;
sort(id + 1, id + 1 + n, [&](int x, int y)
{ return fun[x].a == fun[y].a ? fun[x].b > fun[y].b : fun[x].a < fun[y].a; });
for (tn = 1; tn < tim; tn <<= 1);
for (int i = 1; i <= n; i++) {
int d = id[i];
if (ed[d] == 0) ed[d] = tim;
update(beg[d], ed[d], d);
}
for (const Qry &q : qry) {
LL t = ask(q.t, q.p);
if (t > 8e18) puts("-1");
else printf("%lld\n", t);
}
qry.clear();
for (int i = 1; i <= tn * 2; i++)
tr[i].clear();
for (int i = 1; i <= n; i++)
beg[i] = ed[i] = 0;
}
return 0;
}
暴力可过题。考虑每种轮廓的外接矩形的面积和不超过 4 × 1 0 8 4 \times 10^8 4×108,直接暴力枚举外接矩形内的所有点,判一下在不在轮廓内即可。判断的话可以看他左边的轮廓线条数是不是奇数,是奇数就在里面,否则就在外面。
#include
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
namespace IO {
const int MAXR = 10000000;
char _READ_[MAXR], _PRINT_[MAXR];
int _READ_POS_, _PRINT_POS_, _READ_LEN_;
inline char readc() {
#ifndef ONLINE_JUDGE
return getchar();
#endif
if (!_READ_POS_) {
if (feof(stdin)) return -1;
_READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
}
char c = _READ_[_READ_POS_++];
if (_READ_POS_ == _READ_LEN_) _READ_POS_ = 0;
return c;
}
template<typename T> inline int read(T &x) {
x = 0; register int flag = 1, c;
while (((c = readc()) < '0' || c > '9') && c != '-')
if (c < 0) return -1;
if (c == '-') flag = -1; else x = c - '0';
while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
x *= flag; return 0;
}
inline int read(char *s) {
register int len = 0, c;
while (isspace(c = readc()) || c <= 0)
if (c < 0) return -1;
s[len++] = c;
while (!isspace(c = readc()) && c) s[len++] = c;
s[len] = 0;
return len;
}
template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
return read(a) | read(x...);
}
inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
inline void printc(char c) {
if (!c) return;
_PRINT_[_PRINT_POS_++] = c;
if (_PRINT_POS_ == MAXR) ioflush();
}
template<typename T> inline void print(T x, char c = '\n') {
if (x < 0) printc('-'), x = -x;
if (x) {
static char sta[20];
register int tp = 0;
for (; x; x /= 10) sta[tp++] = x % 10 + '0';
while (tp > 0) printc(sta[--tp]);
} else printc('0');
printc(c);
}
inline void print(char *s, char c = '\n') {
for (int i = 0; s[i]; i++) printc(s[i]);
printc(c);
}
inline void print(const char *s, char c = '\n') {
for (int i = 0; s[i]; i++) printc(s[i]);
printc(c);
}
template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
print(x, ' '), print(y...);
}
}
const int MAXN = 405, MAXM = 400005;
int mt[MAXN][MAXN], vis[MAXM], n, m, Q, T, tim;
int tag[MAXN][MAXN];
char str[MAXM];
int main() {
for (IO::read(T); T--;) {
IO::read(n, m, Q);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
IO::read(mt[i][j]);
while (Q--) {
int x, y; IO::read(x, y);
int l = IO::read(str), x1 = n, y1 = m, x2 = 0, y2 = m;
for (int i = 0; i < l; i++) {
chkmin(x1, x), chkmin(y1, y);
chkmax(x2, x), chkmax(y2, y);
switch (str[i]) {
case 'L': tag[x--][y + 1] ^= 1; break;
case 'R': tag[++x][y + 1] ^= 1; break;
case 'D': --y; break;
case 'U': ++y; break;
}
}
++x1, ++y1;
++tim;
int ans = 0;
for (int i = x1; i <= x2; i++) {
int t = 0;
for (int j = y1; j <= y2; j++) {
t ^= tag[i][j];
if (t && vis[mt[i][j]] != tim)
vis[mt[i][j]] = tim, ++ans;
tag[i][j] = 0;
}
tag[i][y2 + 1] = 0;
}
IO::print(ans);
}
}
IO::ioflush();
return 0;
}
暴搜题,每种物品至少会选择一个,因此复杂度最坏为 O ( 3 n / 3 ) O(3^{n/3}) O(3n/3)。
#include
using namespace std;
int test,n,k,a[55],b[55],c[55],d[55];
int cnt[55],id[55][55],sa,sb,sc,sd,t,mx;
long long ans;
void dfs(int i)
{
if (i==mx+1)
{
ans=max(ans,1ll*(100+sa)*(100+sb)*(100+sc)*(100+sd));
return;
}
if (!cnt[i])
{
dfs(i+1);
return;
}
for (int j=1;j<=cnt[i];j++)
{
sa+=a[id[i][j]];sb+=b[id[i][j]];sc+=c[id[i][j]];sd+=d[id[i][j]];
dfs(i+1);
sa-=a[id[i][j]];sb-=b[id[i][j]];sc-=c[id[i][j]];sd-=d[id[i][j]];
}
}
int main()
{
scanf("%d",&test);
while(test--)
{
scanf("%d%d",&n,&k);memset(cnt,0,sizeof(cnt));mx=0;
for (int i=1;i<=n;i++)
{
scanf("%d%d%d%d%d",&t,&a[i],&b[i],&c[i],&d[i]);
mx=max(mx,t);
cnt[t]++;id[t][cnt[t]]=i;
}
ans=-1e18;sa=sb=sc=sd=0;
dfs(1);
printf("%lld\n",ans);
}
return 0;
}
这个题和第一场冲了……导致一个 hard 变成了 medium-easy。考虑令一个物品的两种权值为 a i + b i , a i a_i+b_i,a_i ai+bi,ai,就转化成了这一题的第一部分。代码就不放第二次了。
刚开始看成了字符串的编辑距离,写了半天发现没过样例……才发现这题水的不行。
f ( i , j , k ) f(i,j,k) f(i,j,k) 表示最大的 x x x 使得 A [ x . . . i ] A[x...i] A[x...i] 和 B [ 1... j ] B[1...j] B[1...j] 的 LCS 长度至少为 k k k。直接 O ( n 2 ) O(n^2) O(n2) 转移即可,询问的话找到最大的 k k k 使得 f ( r , m , k ) ≥ l f(r,m,k) \ge l f(r,m,k)≥l 即可。复杂度 O ( n m 2 ) O(nm^2) O(nm2)。
#include
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
const int MAXN = 100005;
int f[MAXN][25][25], n, m, Q, T;
char A[MAXN], B[25];
int main() {
for (scanf("%d", &T); T--;) {
scanf("%s%s", A + 1, B + 1);
n = strlen(A + 1), m = strlen(B + 1);
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
f[i][0][0] = i + 1;
for (int j = 1; j <= m; j++) {
f[i][j][0] = i + 1;
if (A[i] == B[j]) for (int k = 1; k <= m; k++)
f[i][j][k] = f[i - 1][j - 1][k - 1];
else for (int k = 1; k <= m; k++)
f[i][j][k] = max(f[i - 1][j][k], f[i][j - 1][k]);
}
}
scanf("%d", &Q);
while (Q--) {
int l, r; scanf("%d%d", &l, &r);
int ans = 0;
for (int k = 1; k <= m; k++)
if (f[r][m][k] >= l) chkmax(ans, k);
else break;
printf("%d\n", r - l + 1 + m - 2 * ans);
}
}
return 0;
}