FJOI泛做

教练找了点 \(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 的两道组合计数题

posted @ 2022-04-25 16:51  fhq_treap  阅读(326)  评论(2)    收藏  举报