zoukankan      html  css  js  c++  java
  • P5284 [十二省联考2019]字符串问题

    题目链接

    SA字典序排序二分 + 线段树优化建图 + 判环 + 拓扑DP最长路

    省选前一天做十二省联考两天T2,当模拟省选,结果写了俩十分部分分还都爆零了(因为一个数组少开了个零,一个似乎特判那里有问题),省选爆炸也可能和这个有关系,今天补上。

    首先转化成最长路模型还是比较容易的,至少省选前我就能想到。A -(支配)- > B, B -(前缀)- > A,然后 S -> A, A -> T,跑一个从 (S)(T) 的最长路即可。如果有环就是-1(因为边权均非负且无零环)

    其中第1,3,4种边比较好搞,点数 (O(n)),边数 (O(m))。但是第 2 种边很麻烦,需要一个B点向所有包含B前缀的A点连边,最大边数能达到 (O(n^2)) 的级别。那么我们需要考虑优化建图。

    子串问题想后缀系列。看起来 SAM 很不可做的样子,我们考虑 SA。如果一些 (A) 点包含 (B) 前缀,那么这些 (A) 点所在的原串后缀一定是一段连续的区间(这个可能有点tbl),但是存在 (A)(B) 的子串的情况的话将会有一些“缺口”。不过我们发现如果那样的话 (A)字典序(不是原串后缀字典序)一定比 (B) 的小,那么我们干脆直接按子串的字典序(A) 排序,查询可以直接二分(lower_bound + half_find)。再加上线段树优化建图,我们就做完了。

    注意一下,这里最长路直接拓扑DP即可,不用SPFA,因为环可以判掉,就直接成一DAG了。

    思维难度省选-,代码难度NOI

    上6.5K代码的部分代码:

    inline void Build()
    inline int get_lcp(int l, int r) //rank l ~ rank r
    int na, nb;
    struct Substring {
    	int l, r, id;
    	inline void Read(int idd) {
    		read(l), read(r); id = idd;
    	}
    	bool operator <(const Substring a) const {
    		int lcp = get_lcp(rk[l], rk[a.l]);
    		int len1 = r - l + 1, len2 = a.r - a.l + 1;
    		if (lcp < len1 && lcp < len2)	return s[l + lcp] < s[a.l + lcp];//Attention!!! : <
    //		if (lcp >= len1 && lcp >= len2)	return ;//Attention!!! : > //Attention!!!!
    		return len1 < len2;
    	}
    }A[N], B[N];
    int mp[N], pos[N];
    int ls[NN], rs[NN], root, ttot;
    void build(int L, int R, int &cur)
    void AddEdge(int L, int R, int l, int r, int from, int cur)
    int dfn[NN + N], low[NN + N], dcnt;
    int stk[NN + N], stop, col[NN + N], ctot;
    bool instk[NN + N];
    void tarjan(int cur)
    
    int S, t;
    int que[NN + N], front, rear;
    ll dis[NN + N];
    inline void get_ans() {
    	memset(dis, 0xc0, sizeof(dis));
    	dis[S] = 0;
    	for (register int i = 1; i <= t; ++i)	if (!d[i])	que[++rear] = i;//Attention!!!!
    	while (front < rear) {
    		int cur = que[++front];
    		for (register int i = head[cur]; i; i = e[i].nxt) {
    			int to = e[i].to;
    			MAX(dis[to], dis[cur] + e[i].val);
    			--d[to];
    			if (!d[to])	que[++rear] = to;
    		}
    	}
    	printf("%lld
    ", dis[t]);
    }
    inline int Find(int st, int l, int len) {//Attention!!!!!
    	int r = na, res = l - 1;
    	while (l <= r) {
    		int mid = (l + r) >> 1;
    		int lcp = get_lcp(rk[A[mid].l], rk[B[st].l]);//Attention!!!!!!
    		if (lcp >= len)	res = mid, l = mid + 1;
    		else	r = mid - 1;
    	}
    	return res;
    }
    inline void work() {
    	scanf("%s", s + 1); slen = strlen(s + 1);
    	Build();
    	read(na);
    	for (register int i = 1; i <= na; ++i) A[i].Read(i);
    	read(nb);
    	for (register int i = 1; i <= nb; ++i)	B[i].Read(i);
    	sort(A + 1, A + 1 + na);
    	for (register int i = 1; i <= na; ++i)	mp[A[i].id] = i;
    	build(1, na, root);
    	int m; read(m);
    	for (register int i = 1; i <= m; ++i) {
    		int a, b; read(a), read(b); a = mp[a];
    		addedge(pos[a], ttot + b, A[a].r - A[a].l + 1);
    	}
    	for (register int i = 1; i <= nb; ++i) {
    		int l = lower_bound(A + 1, A + 1 + na, B[i]) - A;
    		int r = Find(i, l, B[i].r - B[i].l + 1);//Attention!!!!!!
    		if (l > r)	continue;
    		AddEdge(1, na, l, r, i + ttot, root);
    	}
    	S = ttot + nb + 1, t = S + 1;
    	for (register int i = 1; i <= na; ++i) {
    		addedge(S, pos[i], 0);
    		addedge(pos[i], t, A[i].r - A[i].l + 1);
    	}
    	for (register int i = 1; i <= t; ++i) {
    		if (!dfn[i])	tarjan(i);
    	}
    	if (ctot != t) { puts("-1"); return ; }
    	get_ans();
    }
    
  • 相关阅读:
    教程-Supports判断接口(Instance)是否支持
    Android实例-如何使用系统剪切板(XE8+小米2)
    Android实例-从照相机或图库获取照片(XE8+小米2)
    Android实例-TTabControl的使用(XE8+小米2)
    Android实例-闪光灯的控制(XE8+小米2)
    WCF小问题总汇
    wpf程序线程与UI内容交互
    xml解析原理一些想法
    XML序列化
    C#用正则表达式一键Unicode转UTF8(解决LitJson中文问题)
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13519649.html
Copyright © 2011-2022 走看看