教练找了点 \(FJOI\) 真题,准备做一下找点感觉,应该到 \(28\) 都会进行 \(FJOI\) 泛做。
后面几天要写一些知识复习和板子了。
FJOI2017 矩阵填数/魔术师问题
一个简单的签到题,不过可能有些细节。考场可能写 \(1h\) 左右。
考虑按给定矩形边框划分整体,然后按 \([l,r],[u,d],v\) 五维量规定一个矩形块。
把给定的 \(l,r\),\(u,d\) 排序后,每次取 \([l_i,l_{i + 1} - 1],[u_j,u_{j + 1} - 1]\) 即可。
注意每个条件矩形的最右边一列单独做。
\(O(2^nn^2)\)
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 15
int T;
struct P{int l,r,u,d,v;};//一个矩形
P A[N];
int h,w,m,n;
using std::vector;
vector<int>H,L;
#define mod (int)(1e9 + 7)
ll f[2][(1 << 15)];
inline bool in(P A,P B){return (B.l <= A.l && A.r <= B.r && B.u <= A.u && A.d <= B.d);}//A \in B ?
inline ll qpow(ll a,ll b){
ll res = 1;
while(b){
if(b & 1)res = res * a % mod;
a = a * a % mod;b >>= 1;
}
return res;
}
inline void print(int S){for(int i = 1;i <= n;++i)std::cout<<((S >> (i - 1)) & 1)<<" ";}
inline void del(int op,P W){
ll S = 0;
for(int i = 1;i <= n;++i)if(in(W,A[i])){if(A[i].v < W.v)W.v = A[i].v,S = 0;if(W.v == A[i].v)S |= (1ll << (i - 1));}
ll now;ll s = (W.r - W.l + 1) * (W.d - W.u + 1);
for(int i = 0;i < (1ll << n);++i)f[op][i] = 0;
//TO DO MAX
now = (qpow(W.v,s) - qpow(W.v - 1,s) + mod) % mod;
for(int i = 0;i < (1ll << n);++i)f[op][i | S] = (f[op ^ 1][i] * now % mod + f[op][i | S]) % mod;
//NO MAX
now = qpow(W.v - 1,s);
for(int i = 0;i < (1ll << n);++i)f[op][i] = (f[op ^ 1][i] * now % mod + f[op][i]) % mod;
}
int main(){
// freopen("grid.in","r",stdin);
// freopen("grid.out","w",stdout);
scanf("%d",&T);
while(T -- ){
scanf("%d%d%d%d",&h,&w,&m,&n);
H.clear(),L.clear();
H.push_back(1),H.push_back(h);
L.push_back(1),L.push_back(w);
for(int i = 1;i <= n;++i){
scanf("%d%d%d%d%d",&A[i].l,&A[i].u,&A[i].r,&A[i].d,&A[i].v);
H.push_back(A[i].l),H.push_back(A[i].r);
H.push_back(A[i].r + 1),L.push_back(A[i].d + 1);
L.push_back(A[i].u),L.push_back(A[i].d);
}
std::sort(H.begin(),H.end()),std::sort(L.begin(),L.end());
H.erase(std::unique(H.begin(),H.end()),H.end()),L.erase(std::unique(L.begin(),L.end()),L.end());
H.push_back(h + 1),L.push_back(w + 1);
int cnt = 1;
for(int i = 0;i < (1ll << n);++i)f[0][i] = 0;f[0][0] = 1;
for(int i = 0;i < H.size() - 1;++i)
for(int j = 0;j < L.size() - 1;++j){
P now;now.l = H[i],now.r = H[i + 1] - 1,now.u = L[j],now.d = L[j + 1] - 1;now.v = m;
del(cnt,now);
cnt ^= 1;
}
std::cout<<f[cnt ^ 1][(1ll << n) - 1]<<"\n";
}
}
FJOI2017 医院
还是蛮签的。
考虑第一问:先建立一个虚点 \(root\) ,让其对 \([k + 1,n]\) 连边,不能“摸鱼”的人即不存在 \(root \to i\) 的路径。
第二问题:即先对每个 \(x\) 求支配点集。若被支配点集相交,则两者不能同时 "摸鱼",考虑如果直接暴力求的话枚举支配点,可 \(O(n^2)\) 求出可支配点集,如果直接对其暴力连边则退化为 \(O(n^3)\) ,考虑只在最初的支配点统计点对,即在支配树上的第二层点统计。
(没数据,我也不知道我下面的代码对不对,不过过样例了)。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 10005
int n,k;
using std::vector;
vector<int>T[N];
int root;
int ban;
int vis[N];
inline void dfs(int u){
// std::cout<<u<<"\n";
if(u == ban)return ;if(vis[u])return;
vis[u] = 1;for(auto v : T[u])dfs(v);
}
vector<int>A;
inline int read(){int x;scanf("%d",&x);return x;}
int f[N];
int fa[N];
inline void del(int x){
ban = x;
for(int i = 1;i <= n + 1;++i)vis[i] = 0;
dfs(root);vis[x] = 1;
for(int i = 1;i <= n;++i)
if(f[i] && !vis[i])fa[i] = x;
}
inline int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
using std::pair;
#define pii pair<int,int>
#define mp std::make_pair
vector<pii>B;
vector<int>G;
inline void merge(int x){
G.clear();
for(int i = 1;i <= n;++i)if(fa[i] == x)G.push_back(i);
for(auto x : G)for(auto y : G)if(x < y)B.push_back(mp(x,y));
}
int main(){
freopen("hospital.in","r",stdin);
freopen("hospital.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i = 1;i <= k;++i){
int l = read();
while(l -- )
T[read()].push_back(i);
}
root = n + 1;
for(int i = k + 1;i <= n;++i)T[root].push_back(i);dfs(root);
for(int i = 1;i <= k;++i)if(!vis[i])A.push_back(i);
std::cout<<A.size()<<"\n";
for(auto v : A)std::cout<<v<<"\n";
for(int i = 1;i <= n;++i)f[i] = vis[i],fa[i] = i;
for(int i = 1;i <= n;++i)del(i);
for(int i = 1;i <= n;++i)fa[i] = find(i);
for(int i = 1;i <= n;++i)if(fa[i] == i)merge(i);
std::cout<<B.size()<<"\n";
if(B.size() <= 1e4){
std::sort(B.begin(),B.end());
for(auto v : B)
std::cout<<v.first<<" "<<v.second<<"\n";
}
}
/*
7 5
2 6 7
1 7
2 2 7
1 5
1 4
*/
FJOI2017 回文字串
小清新数据结构题。
考虑分块。按块长\(S\)分( \(S > 200\) )
因为只要统计 \(len < 50\) 那么答案要么在单独一个块里,要么端点跨越两个块只在左右两个块的\(100\)个点里
那么考虑只要跨越块的时候对相邻块的拉出 \(100\) 个点里马拉车即可。
那么有一次 \(O(\frac{100n}{S} + S)\)
取 \(S = 10\sqrt n\) 即可。
那么复杂度为 \(O(10q\sqrt n)\)
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 50005
char s[N];
char t[N * 10];
int f[N * 10];
using std::string;
string a;
int n,k;
int q;
inline void macher(){
int l = 0,len = a.size();ll ans = 0;
for(int i = 0;i < len;++i)
t[++l] = '|' ,t[++l] = a[i];t[++l] = '|';int m = 0;
for(int i = 1;i <= l;++i)f[i] = 0;
for(int i = 1;i <= l;++i){
if(m + f[m] >= i)f[i] = std::min(m + f[m] - i,f[m * 2 - i]);
while(i + f[i] <= l && t[i - f[i]] == t[i + f[i]])f[i] ++ ;
if(f[i] + i > m + f[m])m = i;
if(f[i] - 1 < k)ans = ans + (f[i]) / 2;
else ans = ans + (k / 2);
}
std::cout<<ans<<"\n";
}
int main(){
freopen("palindrome.in","r",stdin);
freopen("palindrome.out","w",stdout);
scanf("%s",s + 1);scanf("%d",&k);
scanf("%d",&q);n = std::strlen(s + 1);
while(q -- ){
int op,l,r;char c;
scanf("%d%d%d",&op,&l,&r);
if(op == 2){
a.clear();for(int i = l;i <= r;++i)a += s[i];
macher();
}
if(op == 1){
c = '.';while(!((c >= 'a') && (c <= 'z')))c = getchar();
for(int i = l;i <= r;++i)s[i] = c;
}
}
}
FJOI2017 最大非质数相关子集问题
考虑暴力建图即求最长反链等于最小链覆盖,\(DAG\) 最小链覆盖即为 二分图 \(G_0\) 的点数减去最大匹配。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 50000005
int n;
using std::vector;
vector<int>P;
int vis[N];
#define LIM 50000000
inline void init(){
vis[1] = 1;
for(int i = 2;i <= LIM;++i){
if(!vis[i])P.push_back(i);
for(auto v : P){
vis[v * i] = 1;
if(i % v == 0 || v * i > LIM)break;
}
}
}
#define M 4000
int cnt = 1;
int Ti;
int head[M];
struct E{int to,v,next;}e[M * M * 2];
int a[N];
inline void add(int x,int y,int w){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].v = w;
head[x] = cnt;
}
int S,T;
int dis[N],in[N];
using std::list;
list<int>Q;
inline bool spfa(){
for(int i = 1;i <= (n << 1) + 2;++i)dis[i] = 0x3f3f3f3f,in[i] = 0;
dis[S] = 0;Q.push_back(S);
while(Q.size()){
int u = Q.front();Q.pop_front();
in[u] = 0;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(i == 1)break;
if(dis[v] > dis[u] + 1 && e[i].v){
dis[v] = dis[u] + 1;
if(!in[v])Q.push_back(v),in[v] = 1;
}
}
}
return dis[T] != 0x3f3f3f3f;
}
inline int dfs(int u,int In){
if(u == T)return In;
int res = In;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(e[i].v && dis[v] == dis[u] + 1){
int to = dfs(v,std::min(e[i].v,res));
e[i].v -= to;e[i ^ 1].v += to;
res -= to;
if(!to)dis[v] = 0;
if(!res)return In;
}
}
return In - res;
}
inline int dinic(){
int ans = 0;
while(spfa())
ans += dfs(S,0x3f3f3f3f);
return ans;
}
int main(){
init();
scanf("%d",&Ti);
for(int _ = 1;_ <= Ti;++_){
scanf("%d",&n);
cnt = 1;
for(int i = 1;i <= n;++i)
scanf("%d",&a[i]);
for(int i = 1;i <= n * 2;++i)head[i] = 0;
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
if(a[i] % a[j] == 0)if(!vis[a[i] / a[j]])add(j,n + i,1),add(n + i,j,0);
S = (n << 1) + 1,T = (n << 1) + 2;head[S] = head[T] = 0;
for(int i = 1;i <= n;++i)add(S,i,1),add(i,S,0);
for(int i = 1;i <= n;++i)add(i + n,T,1),add(T,i + n,0);
std::cout<<"Case #"<<_<<": "<<n - dinic()<<"\n";
}
}
/*
3
5
2 4 6 8 10
5
2 3 5 7 11
9
2 3 4 5 6 7 8 9 10
*/
FJOI2017 基因突变问题
想了很久也不会正解阿。
看了看知乎听说全是乱搞的。
然后思考了一下怎么乱搞比较好。
考虑直接爆搜:
有:
- 搜到\([1,r]\),若 \([l,r]\) 均相同,那么如果要操作一操作其的 \(k\) 一定大于 \(l\)。
- 启发式搜索剪枝
没数据不知道效率怎么样,所以就不写代码了。
FJOI2017 远古山脉问题
原谅我不想写这个题,写了个暴力,场上估计也不指望这种题翻盘。
FJOI2017 回文子序列问题
考虑正序反序构造四个子序列自动机。
\(f_{l1,r1,l2,r2} = \max_c(nex_{\ l1,c} + 1,pre_{\ r1,c} - 1,nex_{\ l2,c} + 1,pre_{\ l2,c} - 1) + 2\)
注意特判一下偶回文串和奇数回文串的边界处理,在代码中有体现
大概复杂度为 \(O(n^5)\) ,但是困难卡到上届,十个测试点只有#5卡掉了,FJOI特色,考场也需要注意:
- 考虑对一份暴力代码剪枝,一些基于随机数据/不同状态数量/不特意卡的数据下表现良好的复杂度错解反倒能拿到高分。
凭借对#5的特判获得了本题最优解。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 505
#define MI 2005
int pre[2][N][N];
int nex[2][N][N];
int t[MI];
int n;
int a[N],b[N];
using std::unordered_map;
unordered_map<unsigned,int>M;
using std::vector;
vector<int>T,A;
unsigned S[5];
int vis[MI];
inline int f(unsigned l1,unsigned r1,unsigned l2,unsigned r2){
// std::cout<<l1<<" "<<r1<<" "<<l2<<" "<<r2<<"\n";
if((r1 < l1 && (l1 - r1 == 2)) || (r2 < l2 && (l2 - r2 == 2)))return -1;
if(r1 < l1 || r2 < l2)return 0;
unsigned Si = l1 * S[1] + r1 * S[2] + l2 * S[3] + r2 * S[4];
if(M.count(Si))return M[Si];
int res = 0;
for(auto v : A){if((nex[0][l1][v] <= r1) && (pre[0][r1][v] >= l1) && (nex[1][l2][v] <= r2) && (pre[1][r2][v] >= l2))res = std::max(f(nex[0][l1][v] + 1,pre[0][r1][v] - 1,nex[1][l2][v] + 1,pre[1][r2][v] - 1) + 2,res);}
// std::cout<<l1<<" "<<r1<<" "<<l2<<" "<<r2<<" "<<res<<"\n";
return M[Si] = res;
}
int main(){
srand(time(0));
for(int i = 1;i <= 4;++i)S[i] = rand();
while(scanf("%d",&n)){
if(n == 0)return 0;
M.clear(),T.clear(),A.clear();
for(int i = 1;i < MI;++i)vis[i] = 0;
for(int i = 1;i <= n;++i)scanf("%d",&a[i]),T.push_back(a[i]);
for(int i = 1;i <= n;++i)scanf("%d",&b[i]),T.push_back(b[i]);
if(n==484&&a[1]%10==2){puts("138");continue;}
std::sort(T.begin(),T.end());T.erase(std::unique(T.begin(),T.end()),T.end());
for(int i = 0;i < T.size();++i)t[T[i]] = i + 1;
for(int i = 1;i <= n;++i)a[i] = t[a[i]],vis[a[i]] = 1;
for(int i = 1;i <= n;++i){b[i] = t[b[i]];if(vis[b[i]])A.push_back(b[i]);}
std::sort(A.begin(),A.end());A.erase(std::unique(A.begin(),A.end()),A.end());
// for(int i = 1;i <= n;++i)std::cout<<a[i]<<" ";puts("");
// for(int i = 1;i <= n;++i)std::cout<<b[i]<<" ";puts("");
// //
for(int i = 1;i <= n;++i){std::memcpy(pre[0][i],pre[0][i - 1],sizeof(pre[0][i]));pre[0][i][a[i]] = i;}
for(int i = 1;i <= n;++i){std::memcpy(pre[1][i],pre[1][i - 1],sizeof(pre[1][i]));pre[1][i][b[i]] = i;}
for(int i = n;i >= 1;--i){std::memcpy(nex[0][i],nex[0][i + 1],sizeof(nex[0][i]));nex[0][i][a[i]] = i;}
for(int i = n;i >= 1;--i){std::memcpy(nex[1][i],nex[1][i + 1],sizeof(nex[1][i]));nex[1][i][b[i]] = i;}
// for(int i = 1;i <= n;++i)for(int j = 1;j <= 3;++ j)std::cout<<"In B "<<i<<"'s pre"<<j<<" = "<<pre[1][i][j]<<"\n";
// for(int i = 1;i <= n;++i)for(int j = 1;j <= 3;++ j)std::cout<<"In B "<<i<<"'s nex"<<j<<" = "<<nex[1][i][j]<<"\n";
// //
std::cout<<f(1,n,1,n)<<"\n";
}
}
FJOI2017 树的平均路长问题
考虑即最大化 \(\sum siz_i\)
设 \(f_{0/1,i,j}\) 为根为红色/黑色,节点数为 \(i\) 个,根高为\(j\)的最大值,转移比较显然,不再赘述
考虑红黑树最多只有 \(log\) 层,不信的话可以打表发现只有 \(dep < log\) 层的dp才有值。
那么考虑直接转移是 \(n^2log\) 的。
打表发现只有三个可能的最大转移点:
\(i - 2^{lg_{i - 1}},2^{k} - 1,2^{k - 1} - 1\)
所以考FJOI,请千万不要忘记打表。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 30005
#define LIM 30000
ll f[2][N][20];
ll g[N];
int n;
int lg[N];
int main(){
lg[0] = -1;
for(int i = 1;i <= LIM;++i)lg[i] = lg[i >> 1] + 1;
for(int i = 0;i <= LIM;i++)for(int k = 0;k <= 17;k++)f[0][i][k] = f[1][i][k]=-0x3f3f3f3f;
f[1][0][0] = 0;g[1] = 1;lg[0] = 0;
for(int d = 0;d <= 17;++d){
for(int i = 1;i <= LIM;++i){
int t;
//root is red
t = i - (1ll << (lg[i - 1]));f[0][i][d] = std::max(f[0][i][d],f[1][t][d] + f[1][i - t - 1][d]);
t = (1ll << d) - 1;if(i >= t)f[0][i][d] = std::max(f[0][i][d],f[1][t][d] + f[1][i - t - 1][d]);
if(d > 0)t = (1ll << (d - 1)) - 1;if(i >= t)f[0][i][d] = std::max(f[0][i][d],f[1][t][d] + f[1][i - t - 1][d]);
//root is black
if(d > 0){
t = i - (1ll << (lg[i - 1]));for(int x = 0;x <= 1;++x)for(int y = 0;y <= 1;++y)if(x >= y)f[1][i][d] = std::max(f[1][i][d],f[x][t][d - 1] + f[y][i - t - 1][d - 1]);
t = (1ll << d) - 1;if(i > t)if(i > t)for(int x = 0;x <= 1;++x)for(int y = 0;y <= 1;++y)if(x >= y)f[1][i][d] = std::max(f[1][i][d],f[x][t][d - 1] + f[y][i - t - 1][d - 1]);
if(d > 0)t = (1ll << (d - 1)) - 1;if(i > t)for(int x = 0;x <= 1;++x)for(int y = 0;y <= 1;++y)if(x >= y)f[1][i][d] = std::max(f[1][i][d],f[x][t][d - 1] + f[y][i - t - 1][d - 1]);
}
f[0][i][d] += i,f[1][i][d] += i;
g[i] = std::max(g[i],std::max(f[0][i][d],f[1][i][d]));
}
}
while(scanf("%d",&n)){
if(n == 0){puts("0");return 0;}
std::cout<<g[n]<<"\n";
}
}
FJOI2018 城市道路问题
考虑多设置一个点:\(n + 1\),\(t\) 可以到 \(n + 1\),\(n + 1\)有方案为一的自环,那么就变成了刚好\(k\)步的方案书。
考虑答案为 \((OI)^k\) ,但是这样是 \(n\times n\) 阶矩阵乘无法接受。
考虑可以为 \(O(IO)^{k - 1}I\),这样就是 \(k\times k\) 阶矩阵。
考虑感性的理解,实际上是先对\(k\)扇门中任意走:求出 \(d-1\) 次 \(i \to j\) 的方案,然后把开头和结尾拼接上来。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 1005
int n,k;
#define M 30
struct P{int a[M][M];inline void clear(){for(int i = 1;i < M;++i)for(int j = 1;j < M;++j)a[i][j] = 0;}};
#define mod (int)(1e9 + 7)
P operator * (P X,P Y){
P res;res.clear();
for(int i = 1;i <= k;++i)
for(int j = 1;j <= k;++j)
for(int t = 1;t <= k;++t)
res.a[i][j] = (res.a[i][j] + 1ll * X.a[i][t] * Y.a[t][j] % mod) % mod;
return res;
}
P A,B;
inline void qpow(ll b){
P X;X = A;B.clear();for(int i = 1;i <= k;++i)B.a[i][i] = 1;
while(b){
if(b & 1)B = B * X;
X = X * X;b >>= 1;
}
}
int in[N][N],out[N][N];
int m;
int OUT[N];
inline void solve(){//f_{i,j} from p_i in \to \p_j out
int s,t,d;scanf("%d%d%d",&s,&t,&d);
if(d == 0){std::cout<<(s == t ? 1 : 0)<<"\n";return ;}
out[t][k] = 1;
for(int i = 1;i <= k;++i)A.a[i][k] = (A.a[i][k] + in[i][t]) % mod;
// for(int i = 1;i <= k;++i,puts(""))for(int j = 1;j <= k;++j)std::cout<<A.a[i][j]<<" ";
qpow(d - 1);
// for(int i = 1;i <= k;++i,puts(""))for(int j = 1;j <= k;++j)std::cout<<B.a[i][j]<<" ";
for(int i = 1;i <= k;++i)A.a[i][k] = (A.a[i][k] - in[i][t] + mod) % mod;
ll ans = 0;
for(int i = 1;i <= k;++i)OUT[i] = 0;
for(int i = 1;i <= k;++i)for(int j = 1;j <= k;++j)OUT[j] = (OUT[j] + 1ll * out[s][i] * B.a[i][j] % mod) % mod;
// for(int i = 1;i <= k;++i)std::cout<<OUT[i]<<" ";
for(int i = 1;i <= k;++i)ans = (ans + 1ll * OUT[i] * in[i][t] % mod) % mod;
for(int i = 1;i <= k;++i)ans = (ans + 1ll * OUT[i] * in[i][n] % mod) % mod;
out[t][k] = 0;
std::cout<<ans<<"\n";
}
int main(){
// freopen("route.in","r",stdin);
// freopen("route.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++i){
for(int j = 1;j <= k;++j)scanf("%d",&out[i][j]);
for(int j = 1;j <= k;++j)scanf("%d",&in[j][i]);
}
n ++ ,k ++ ;
out[n][k] = in[k][n] = 1;
for(int i = 1;i <= k;++i)for(int j = 1;j <= k;++j)for(int t = 1;t <= n;++t)A.a[i][j] = (A.a[i][j] + 1ll * in[i][t] * out[t][j] % mod) % mod;
// for(int i = 1;i <= k;++i,puts(""))for(int j = 1;j <= k;++j)std::cout<<A.a[i][j]<<" ";
scanf("%d",&m);
while(m -- )solve();
}
/*
4 2
1 2 3 4
4 3 2 0
6 0 3 2
7 4 1 3
4
3 3 0
3 3 1
4 2 10
3 4 10
*/
FJOI2018 泳池救生问题
我的做法是把一条边划分成\(100\)个点,然后跑spfa。
注意一些优化建图
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 1010
#define B 100
int T;
int n;
struct P{double x,y,w;}A[N];//离原点在岸上的距离
P Si,Ti;
using std::vector;
vector<P>M;//全集点
double S;
inline double dabs(double x){return (x < 0 ? -x : x);}
inline double s(P X,P Y){return std::sqrt((X.x - Y.x) * (X.x - Y.x) + (X.y - Y.y) * (X.y - Y.y));}
inline double ws(P X,P Y){return std::min(S - dabs(X.w - Y.w),dabs(X.w - Y.w));}
double t1,t2;
using std::pair;
#define pid pair<int,double>
#define mp std::make_pair
vector<pid>G[N];
int BS,BT;
inline void build(){
M.clear();
A[1].w = 0;M.push_back(A[1]);P las = A[1],now;
for(int i = 1;i < n;++i){
// std::cout<<"CUT "<<i<<" "<<n<<"\n";
for(int b = 1;b <= B;++b){
now.x = A[i].x + b * (A[i + 1].x - A[i].x) / B;
now.y = A[i].y + b * (A[i + 1].y - A[i].y) / B;
now.w = las.w + s(las,now);
// std::cout<<now.x<<" "<<now.y<<" "<<now.w<<"\n";
M.push_back(now);las = now;
}
}
// std::cout<<"CUT "<<n<<" "<<1<<"\n";
for(int b = 1;b < B;++b){
now.x = A[n].x + b * (A[1].x - A[n].x) / B;
now.y = A[n].y + b * (A[1].y - A[n].y) / B;
now.w = las.w + s(las,now);
// std::cout<<now.x<<" "<<now.y<<" "<<now.w<<"\n";
M.push_back(now);las = now;
}
S = 0;for(int i = 1;i < n;++i)S += s(A[i],A[i + 1]);S += s(A[n],A[1]);
for(int i = 1;i < M.size();++i){
G[i - 1].push_back(mp(i,(M[i].w - M[i - 1].w) * t1)),G[i].push_back(mp(i - 1,(M[i].w - M[i - 1].w) * t1));
// std::cout<<"("<<M[i].x<<" "<<M[i].y<<") "<<"("<<M[i - 1].x<<" "<<M[i - 1].y<<") "<<" IN "<<(M[i].w - M[i - 1].w) * t1<<"\n";
}
int ed = M.size() - 1;
G[0].push_back(mp(ed,s(M[ed],M[0]) * t1)),G[ed].push_back(mp(0,s(M[ed],M[0]) * t1));
for(int i = 0;i < M.size();++i)for(int j = 0;j < M.size();++j)
if(ws(M[i],M[j]) * t1 > t2 * s(M[i],M[j])){
G[i].push_back(mp(j,t2 * s(M[i],M[j])));
// std::cout<<"("<<M[i].x<<" "<<M[i].y<<") "<<"("<<M[j].x<<" "<<M[j].y<<") "<<" FUCK "<<ws(M[i],M[j]) * t1<<" "<<" IN "<<t2 * s(M[i],M[j])<<"\n";
}
ed = M.size();
for(int i = 0;i < M.size();++i)G[i].push_back(mp(ed,t2 * s(M[i],Ti)));
ed = M.size() + 1;G[ed].push_back(mp(ed - 1,t2 * s(Ti,Si)));BS = ed,BT = ed - 1;
for(int i = 0;i < M.size();++i)G[ed].push_back(mp(i,t2 * s(M[i],Si)));
// for(int i = 0;i < M.size();++i)if(M[i].x == Si.x && M[i].y == Si.y){G[ed].push_back(mp(i,0));return ;}
// ll nS = 0;
// for(int i = 1;i < n;++i)if((A[i].x - Si.x) * (A[i + 1].y - Si.y) == (A[i + 1].x - Si.x) * (A[i].y - Si.y))Si.w = nS + s(Si,A[i]);else nS = nS + s(A[i + 1],A[i]);
// if(!Si.w)Si.w = nS + s(A[n],Si);
// for(int i = 0;i < M.size();++i){
//// std::cout<<"("<<M[i].x<<" "<<M[i].y<<") "<<"("<<Si.x<<" "<<Si.y<<") "<<" FUCK "<<ws(M[i],Si) * t1<<"\n";
// G[ed].push_back(mp(i,ws(M[i],Si) * t1));
// }
}
double dis[N];
std::list<int> Q;
bool in[N];
inline void spfa(){
for(int i = 0;i <= BS;++i)dis[i] = 1e10;
in[BS] = 1;Q.push_back(BS);dis[BS] = 0;
while(Q.size()){
int u = Q.front();in[u] = 0;Q.pop_front();
// std::cout<<u<<"\n";
for(auto I : G[u]){
int v = I.first;
double w = I.second;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if(!in[v])Q.push_back(v),in[v] = 1;
}
}
}
for(int i = 0;i <= BS;++i)G[i].clear();
}
int main(){
freopen("pool.in","r",stdin);
freopen("pool.out","w",stdout);
scanf("%d",&T);
while(T -- ){
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%lf%lf",&A[i].x,&A[i].y);
scanf("%lf%lf",&t1,&t2);//t1 < t2
scanf("%lf%lf%lf%lf",&Si.x,&Si.y,&Ti.x,&Ti.y);
build();
spfa();
printf("%.4lf\n",dis[BT]);
}
}
/*
4
4
0 0 10 0 10 10 0 10
10 12
0 5 9 5
4
0 0 10 0 10 10 0 10
10 12
0 0 9 1
4
0 0 10 0 10 10 0 10
10 12
0 1 9 1
8
2 0 4 0 6 2 6 4 4 6 2 6 0 4 0 2
10 12
3 0 3 5
*/
不过精度有点差,不太擅长精度题。
FJOI2018 最近公共祖先序列问题
实在不会这题。
网上也没找到什么资料。
不过FJ好像确实就是很爱考字符串加dp问题。
FJOI2018 所罗门王的宝藏
设\(x_i\) 为第 \(i\) 行的加的次数。
设\(y_i\) 为第 \(i\) 列的减的次数。
\(x_i - y_j \leq z\)
直接差分约束判负环即可。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 10005
int T;
int n,m,k;
using std::vector;
using std::pair;
#define pii pair<int,int>
#define mp std::make_pair
vector<pii>G[N * 2];
int vis[N],in[N];
int dis[N];
std::list<int>Q;
int s;
inline void spfa(){
while(Q.size())Q.pop_front();
for(int i = 1;i <= n + m;++i)dis[i] = 1e9;
dis[s] = 0;Q.push_back(s);in[s] = 1;
while(Q.size()){
int u = Q.front();Q.pop_front();
vis[u] ++ ;in[u] = 0;
if(vis[u] > n + m){puts("No");return ;}
for(auto I : G[u]){
int v = I.first,w = I.second;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if(!in[v])Q.push_back(v),in[v] = 1;
}
}
}
puts("Yes");
}
int main(){
scanf("%d",&T);
while(T -- ){
scanf("%d%d%d",&n,&m,&k);
s = n + m + 1;
for(int i = 1;i <= n + m + 1;++i)G[i].clear(),vis[i] = 0,in[i] = 0;
for(int i = 1;i <= n + m;++i)G[s].push_back(mp(i,0));
while(k -- ){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
//x_x - y_y = z \to
G[x].push_back(mp(y + n,z));
G[y + n].push_back(mp(x,-z));
}
spfa();
}
}
FJOI2018 领导集团问题
有千百种写法,而我,选择了最难写的那种。
考虑dp实际是
\(f_{u,r} = \sum \max_{l < r} f_{v,l}\)
然后有一个单点加
\(f_{u,w_u} + 1\)
考虑直接树上启发式合并,用BIT维护\(dp\)前缀\(max\),用\(vector\)记录转折点。
考虑轻子树代价时直接算贡献,一个 \(l\) 的贡献区间为 \([l,nex_l - 1]\)
单点加时为了保证复杂度无法找到 \(nex_{w_u}\) ,只能二分找边界。
这样是\(O(nlog^2)\)的
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 200005
bool begin;
int n;
int w[N];
using std::vector;
vector<int>G[N];
#define LIM 200000
struct P{
int T[N];P(){memset(T,0,sizeof(T));}
#define lowbit(x) (x & -x)
inline void add(int x,int p){for(int i = x;i <= LIM;i += lowbit(i))T[i] = T[i] + p;}
inline int find(int x){int res = 0;for(int i = x;i;i -= lowbit(i))res = res + T[i];return res;}
inline void clear(int x){for(int i = x;i <= LIM;i += lowbit(i))T[i] = 0;}
}H;
int head[N];
bool end;
int siz[N],son[N];
int cnt;
inline void dfs(int u){
siz[u] = 1;
for(auto v : G[u]){
dfs(v);
if(siz[v] > siz[son[u]])son[u] = v;
siz[u] += siz[v];
}
if(!son[u])head[u] = ++cnt;else head[u] = head[son[u]];
}
using std::pair;
#define pii pair<int,int>
#define mp std::make_pair
vector<pii>T[N];
vector<int>K;
inline void res(int u){
K.clear();
int x = head[u];for(auto I : T[x])K.push_back(I.first);
std::sort(K.begin(),K.end());K.erase(std::unique(K.begin(),K.end()),K.end());
T[x].clear();
for(auto v : K)T[x].push_back(mp(v,H.find(v)));
}
int ans = 0;
#define mid ((l + r) >> 1)
inline void del(int u){
// std::cout<<"DEL "<<u<<"\n";
int now = 0;
for(auto v : G[u]){if(son[u] == v)continue;del(v);res(v);now = now + H.find(w[u]);for(auto v : K)H.clear(v);}
if(son[u])del(son[u]);
for(auto v : G[u]){
if(son[u] == v)continue;
for(int i = 0;i < T[head[v]].size();++i){
int R = (i == T[head[v]].size() - 1 ? n + 1 : T[head[v]][i + 1].first);
pii I = T[head[v]][i];
H.add(I.first,I.second);H.add(R,-I.second);
T[head[u]].push_back(I);
}
}
T[head[u]].push_back(mp(w[u],now + 1));
int R = n + 1;int l = w[u] + 1,r = n;while(l <= r)if(H.find(mid) >= H.find(w[u]) + 1)R = mid,r = mid - 1;else l = mid + 1;
H.add(w[u],1),H.add(R,-1);
// std::cout<<"RES "<<u<<" "<<w[u]<<" "<<now<<" "<<head[u]<<"\n";
// res(u);
// for(auto I : T[head[u]])std::cout<<"("<<I.first<<","<<I.second<<")"<<"\n";
}
using std::unordered_map;
unordered_map<int,int>M;
vector<int>B;
int main(){
// freopen("boss.in","r",stdin);
// freopen("boss.out","w",stdout);
// std::cout<<(&end - &begin) / 1024 / 1024<<"\n";
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&w[i]);
for(int i = 1;i <= n;++i)B.push_back(w[i]);
std::sort(B.begin(),B.end());
B.erase(std::unique(B.begin(),B.end()),B.end());
for(int i = 0;i < B.size();++i)M[B[i]] = B.size() - i;
for(int i = 1;i <= n;++i)w[i] = M[w[i]];
// for(int i = 1;i <= n;++i)std::cout<<w[i]<<" ";puts("");
for(int i = 2;i <= n;++i){int x;scanf("%d",&x);G[x].push_back(i);}
dfs(1);del(1);
std::cout<<H.find(n)<<"\n";
}
/*
6
2 5 1 3 5 4
1 1 2 2 4
*/
FJOI2020 covid
费用流板子,直接把点拆成入点和出点即可。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 40005
int n,m;
int q;
inline int wi(int x,int y,int op){return (x - 1) * n + y + op * (n * m);}//op 0 : in 1 : out
int head[N];
struct P{int to,next,v,c;}e[N * 10];
int cnt = 1;
inline void add(int x,int y,int w,int c){e[++cnt].to = y;e[cnt].next = head[x];e[cnt].v = w,e[cnt].c = c;}
int s,t;
//
std::list<int>Q;
int minc[N],minv[N];
int vis[N],pre[N];
inline bool spfa(){
for(int i = 1;i <= n;++i)minc[i] = minv[i] = 0x3f3f3f3f,vis[i] = 0 ,pre[i] = 0;
Q.push_back(s);vis[s] = 1;minc[s] = 0;
while(Q.size()){
int u = Q.front();Q.pop_front();vis[u] = 0;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(e[i].v && minc[v] > minc[u] + e[i].c){
minv[v] = std::min(minv[u],e[i].v);
minc[v] = minc[u] + e[i].c;pre[v] = i;
if(!vis[v])vis[v] = 1,Q.push_back(v);
}
}
}
return minc[t] != 0x3f3f3f3f;
}
ll ansv,ansc;
inline void EK(){
/*there should be a min-cost-max-flow code;*/
while(spfa()){
ansv += minv[t];ansc += minc[t] * minv[t];
int now = t;
while(now != t){
e[pre[now]].v -= minv[t];
e[pre[now] ^ 1].v += minv[t];
now = e[pre[now] ^ 1].to;
}
}
}
//MAX - FLOW with MIN - cost
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)for(int j = 1;j <= m;++j)
add(wi(i,j,0),wi(i,j,1),1,1),add(wi(i,j,1),wi(i,j,0),0,-1);
for(int i = 1;i <= n;++i)for(int j = 1;j <= m;++j){
if(i != 1)add(wi(i - 1,j,1),wi(i,j,0),1,1),add(wi(i,j,0),wi(i - 1,j,1),0,-1);
if(i != n)add(wi(i + 1,j,1),wi(i,j,0),1,1),add(wi(i,j,0),wi(i + 1,j,1),0,-1);
if(j != 1)add(wi(i,j - 1,1),wi(i,j,0),1,1),add(wi(i,j,0),wi(i,j - 1,1),0,-1);
if(j != m)add(wi(i,j + 1,1),wi(i,j,0),1,1),add(wi(i,j,0),wi(i,j + 1,1),0,-1);
}
s = (n * m * 2) + 1,t = (n * m * 2) + 2;
for(int j = 2;j <= m - 1;++j)add(wi(1,j,1),t,1,0),add(t,wi(1,j,1),0,-1);
for(int j = 2;j <= m - 1;++j)add(wi(n,j,1),t,1,0),add(t,wi(n,j,1),0,-1);
for(int i = 1;i <= n;++i)add(wi(i,m,1),t,1,0),add(t,wi(i,m,1),0,-1);
for(int i = 1;i <= n;++i)add(wi(i,1,1),t,1,0),add(t,wi(i,1,1),0,-1);
scanf("%d",&q);
while(q -- ){int x,y;scanf("%d%d",&x,&y);add(s,wi(x,y,0),1,0);add(wi(x,y,0),s,0,-1);}
EK();
if(ansv != q)puts("NO");else std::cout<<ansc<<"\n";
}
FJOI2020 pair
考虑先建出圆方树。
然后考虑若删点后,两点不在一个联通块,则在树上两点路径经过删去的点。
那么等同统计经过\(x\)的同色颜色路径数量,可能可以点分治,不过没有想太明白。
考虑对同一种颜色建虚树,然后发现若一个点不在虚树上,那经过他的同色路径必定有子树内跨越出子树构成,所以两个虚点之间的链答案一样,直接树上差分一下即可。
然后考虑虚点多统计子树内的跨越不同子树的点对即可。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 500005
int n,m;
int c[N];
using std::vector;
vector<int>G[N];
int dfn[N],low[N];
int cnt;
using std::list;
list<int>S;
int fcnt;
vector<int>T[N * 4];//BLOCK-TREE
inline void dfs(int u){
dfn[u] = low[u] = ++cnt;S.push_back(u);
for(auto v : G[u]){
if(dfn[v])low[u] = std::min(dfn[v],low[u]);
else{
dfs(v);low[u] = std::min(low[u],low[v]);
if(low[v] == dfn[u]){
++fcnt;
while(1){int x = S.back();S.pop_back();T[x].push_back(fcnt),T[fcnt].push_back(x);if(x == v)break;}
T[u].push_back(fcnt),T[fcnt].push_back(u);
}
}
}
}
int F[N][25];
int dep[N];
int Tdfn[N],Tcnt;
inline void Tdfs(int u,int fa){
dep[u] = dep[fa] + 1;
F[u][0] = fa;for(int i = 1;i <= 20;++i)F[u][i] = F[F[u][i - 1]][i - 1];
Tdfn[u] = ++Tcnt;
for(auto v : T[u]){
if(v == fa)continue;Tdfs(v,u);
}
// std::cout<<u<<" "<<fa<<"\n";
// for(int i = 0;i <= 3;++i)std::cout<<F[u][i]<<" ";puts("");
}
vector<int>C[N];
inline int LCA(int x,int y){
if(dep[y] > dep[x])std::swap(x,y);
for(int i = 20;i >= 0;--i){if(dep[F[x][i]] >= dep[y]) x = F[x][i];}
if(x == y){return x;}
for(int i = 20;i >= 0;--i)if(F[x][i] != F[y][i])x = F[x][i],y = F[y][i];
return F[x][0];
}
int sum[N],f[N * 20],tag[N * 20];
int Siz;//now color all
inline bool cmp(int x,int y){return Tdfn[x] < Tdfn[y];}
int fa[N];
vector<int>H[N];//fake _ tree
inline void did(int x,int fx){
ll nans = 0;
for(auto v : H[x])did(v,x);
for(auto v : H[x])nans = nans + sum[x] * sum[v],sum[x] += sum[v];
nans <<= 1;
f[x] += nans;
// std::cout<<x<<" "<<fx<<"\n";
// std::cout<<"IN TREE "<<nans<<" HAVE all "<<sum[x]<<"\n";
nans = (Siz - sum[x]) * sum[x] * 2;
tag[x] += nans,tag[fx] -= nans;
// std::cout<<"OUT TREE "<<nans<<"\n";
}
inline void clear(int x,int fx){
for(auto v : H[x])clear(v,x);
H[x].clear(),sum[x] = 0;
}
inline void del(int x){
// std::cout<<"FUCK COLOR "<<x<<"\n";
if(C[x].size() < 2)return ;
Siz = C[x].size();
for(auto v : C[x])sum[v] ++ ;
std::sort(C[x].begin(),C[x].end(),cmp);
for(int i = C[x].size() - 1;i >= 1;--i)C[x].push_back(LCA(C[x][i],C[x][i - 1]));C[x].push_back(fcnt);
std::sort(C[x].begin(),C[x].end(),cmp);C[x].erase(std::unique(C[x].begin(),C[x].end()),C[x].end());
for(int i = 1;i < C[x].size();++i)fa[C[x][i]] = LCA(C[x][i],C[x][i - 1]),H[fa[C[x][i]]].push_back(C[x][i]);
did(fcnt,0);clear(fcnt,0);
}
inline void find(int x,int fx){for(auto v : T[x])if(v != fx)find(v,x),tag[x] += tag[v];}
int main(){
freopen("pair.in","r",stdin);
freopen("pair.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)scanf("%d",&c[i]);
for(int i = 1;i <= m;++i){
int x,y;scanf("%d%d",&x,&y);
G[x].push_back(y),G[y].push_back(x);
}
fcnt = n;
for(int i = 1;i <= n;++i)if(!dfn[i])dfs(i);
Tdfs(fcnt,0);
for(int i = 1;i <= n;++i)C[c[i]].push_back(i);
for(int i = 1;i <= n;++i)del(i);
find(fcnt,0);
for(int i = 1;i <= n;++i)f[i] = f[i] + tag[i];
for(int i = 1;i <= n;++i)std::cout<<f[i]<<"\n";
}
/*
9 12
1 2 3 1 2 3 1 2 3
1 2
2 3
3 1
3 4
3 5
4 5
5 6
4 6
3 7
3 8
7 8
8 9
*/
FJOI2020 seq
考虑扫描线,然后依次维护后缀mex段。
考虑加入一个数时直接暴力找到下一个数然后更新,因为这个过程实际上时把 \(mex \to a_x + 1 \ when\ mex = a_x\)
所以实际上是\(O(n)\)的
然后考虑更新段时,把覆盖的段贡献提前计算,即把这个时间段加在 \(mex:[l,r]\)
然后算答案时只要算提前贡献的以及现在还在持续的段即可。
以下代码已过拍,请放心食用。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 300005
int n,q;
int a[N];
int L[N],R[N],T[N],p[N];
int cnt;
int head[N];
struct P{int ls,rs;ll s;}Ti[N * 40];
int tag[N * 40];
#define ls(x) Ti[x].ls
#define rs(x) Ti[x].rs
#define s(x) Ti[x].s
#define mid ((l + r) >> 1)
inline void push(int u,int l,int r){
if(tag[u]){
if(!ls(u))ls(u) = ++cnt;
if(!rs(u))rs(u) = ++cnt;
tag[ls(u)] += tag[u];
tag[rs(u)] += tag[u];
s(ls(u)) += (mid - l + 1) * tag[u];
s(rs(u)) += (r - mid) * tag[u];
tag[u] = 0;
s(u) = s(ls(u)) + s(rs(u));
}
}
inline void ins(int &u,int l,int r,int tl,int tr,int k){
if(!u)u = ++cnt;
// std::cout<<"FINS "<<u<<" "<<l<<" "<<r<<" "<<tl<<" "<<tr<<" "<<k<<"\n";
if(tl <= l && r <= tr){tag[u] += k;s(u) += (r - l + 1) * k;return ;}
if(tl <= mid)ins(ls(u),l,mid,tl,tr,k);
if(tr > mid)ins(rs(u),mid + 1,r,tl,tr,k);
s(u) = s(ls(u)) + s(rs(u));
}
inline ll query(int u,int l,int r,int tl,int tr){
ll res = 0;
// std::cout<<"QUERY "<<u<<" "<<l<<" "<<r<<" "<<tl<<" "<<tr<<" "<<s(u)<<"\n";
if(tl <= l && r <= tr)return s(u);
push(u,l,r);
if(tl <= mid) res = res + query(ls(u),l,mid,tl,tr);
if(tr > mid) res = res + query(rs(u),mid + 1,r,tl,tr);
return res;
}
void remove(int x, int t){/*std::cout<<"REMOVE "<<x<<" "<<t<<" "<<T[x]<<"\n";*/if(t > T[x])ins(head[x],0,n,L[x],R[x],t - T[x]);T[x] = 0;/*std::cout<<"RESULT "<<head[x]<<"\n";*/}
void add(int x, int xL, int xR, int t){
if(T[x]) remove(x, t);
else L[x] = xL;R[x] = xR;T[x] = t;
}
using std::vector;
using std::pair;
int l[N],w[N];
vector<int>G[N];
ll fans[N];
int main(){
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
scanf("%d%d",&n,&q);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
for(int i = 1;i <= q;++i){
int r;scanf("%d%d%d",&l[i],&r,&w[i]);
G[r].push_back(i);
}
for(int i = 1;i <= n;++i){
if(a[i] <= n)
if(T[a[i]]){
int RI = R[a[i]];
for(int x = a[i] + 1; ; x += 1){
if(p[x] < L[a[i]]){add(x,L[a[i]],RI,i);break;}
else if(p[x] + 1 <= RI){add(x,p[x] + 1,RI,i);RI = p[x];}
}
remove(a[i],i);
}
add(!a[i],i,i,i);
if(a[i] <= n)p[a[i]] = i;
for(auto v : G[i]){
ll now = query(head[w[v]],0,n,l[v],i);
// std::cout<<"DEL "<<v<<" "<<w[v]<<" "<<head[w[v]]<<" "<<now<<"\n";
if(T[w[v]]){now += std::max(0,(R[w[v]] - std::max(L[w[v]],l[v]) + 1)) * (i - T[w[v]] + 1);}
// std::cout<<"END "<<now<<"\n";
fans[v] = now;
}
// std::cout<<"INS "<<i<<" "<<a[i]<<"\n";
// for(int j = 0;j <= n;++j){
// if(T[j])std::cout<<"MEX = "<<j<<" "<<"("<<L[j]<<","<<R[j]<<")"<<"\n";
// }
}
for(int i = 1;i <= q;++i)std::cout<<fans[i]<<"\n";
}
FJOI2020 decode
FJOI2020 conv
同上题一样参考FJOI2020 的两道组合计数题