• 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 的两道组合计数题

  • 相关阅读:
    W3C help
    css值解析
    css中的格式上下文Formatting Context
    css中绝对定位中的left和top属性
    事件模型
    程序员应该如何更有效率
    css的边偏移距离
    css插入框
    css中的whitespace属性
    源码安装nginx 方法二
  • 原文地址:https://www.cnblogs.com/dixiao/p/16190988.html
Copyright © 2020-2023  润新知