分类: 图论

  • APIO2018 铁人两项

    题意:求满足如下条件的有序三元组个数$(u,v,w)$且u到v的路径与v到w的路径不重叠。

    对原图建立圆方树,然后枚举中间点m,如果存在两个点u和v,满足u到m的路径和v到m的路径不经过同一个圆点。

    考虑容斥,枚举中间点m,然后计算除了m之外的总的有序点对数,然后减掉到达m必须要经过同一个点的点对数

    如何容斥?

    考虑一个方点u的所有相邻的圆点v,v的子树内的一个点对,要想到达u除了v之外的一个相邻节点,则必须经过v。

    所以对于每个方点u,统计其所有相邻圆点v子树内的有序点对数,然后乘u的相邻节点个数-1,即为u的每个相邻节点应该减掉的值的和。

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    #include <assert.h>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    #define debug(x) cout << #x << " = " << x << endl;
    
    const int_t LARGE = 300000;
    
    constexpr inline int_t C2(int_t x)
    {
        return x * (x - 1) / 2;
    }
    struct Edge
    {
        int_t v1, v2;
    };
    
    int_t n, m;
    std::vector<int_t> graph[LARGE + 1];
    std::vector<int_t> tree[LARGE + 1];
    std::vector<int_t> BCC[LARGE + 1];
    std::stack<Edge> stack;
    int_t size[LARGE + 1];
    bool isCut[LARGE + 1];
    int_t DFSN[LARGE + 1];
    int_t LOW[LARGE + 1];
    int_t BCCID[LARGE + 1];
    int_t parent[LARGE + 1];
    int_t block[LARGE + 1];
    int_t nextVtx;
    void DFS(int_t vtx, int_t from = -1)
    {
        DFSN[vtx] = LOW[vtx] = ++DFSN[0];
        int_t chdCount = 0;
        for (int_t to : graph[vtx])
        {
            if (to == from)
                continue;
    
            if (DFSN[to] == 0)
            {
                stack.push(Edge{vtx, to});
                chdCount++;
                DFS(to, vtx);
                LOW[vtx] = std::min(LOW[vtx], LOW[to]);
                //判断不能写LOW[vtx]>=DFSN[vtx],否则会把一棵不是BCC的子树算重
                if (LOW[to] >= DFSN[vtx])
                {
                    isCut[vtx] = true;
                    int_t id = ++BCCID[0];
                    int_t next = ++nextVtx;
                    while (true)
                    {
                        auto top = stack.top();
                        stack.pop();
                        if (BCCID[top.v1] != id)
                        {
                            BCCID[top.v1] = id;
                            BCC[id].push_back(top.v1);
                            tree[next].push_back(top.v1);
                            tree[top.v1].push_back(next);
                        }
                        if (BCCID[top.v2] != id)
                        {
                            BCCID[top.v2] = id;
                            BCC[id].push_back(top.v2);
                            tree[next].push_back(top.v2);
                            tree[top.v2].push_back(next);
                        }
                        if ((top.v1 == vtx && top.v2 == to) || (top.v1 == to && top.v2 == vtx))
                            break;
                    }
                }
            }
            //只判断后向边,防止算重
            else if (DFSN[to] < DFSN[vtx])
            {
                stack.push(Edge{vtx, to});
                LOW[vtx] = std::min(LOW[vtx], DFSN[to]);
            }
        }
        if (from == -1 && chdCount == 1)
            isCut[vtx] = false;
    }
    void DFS1(int_t vtx, int_t block, int_t from = -1, int_t depth = 1)
    {
        static bool visited[LARGE + 1];
        if (visited[vtx])
            return;
        parent[vtx] = from;
        visited[vtx] = true;
        size[vtx] = (vtx <= n);
        ::block[vtx] = block;
        for (int_t to : tree[vtx])
        {
            if (visited[to])
                continue;
            DFS1(to, block, vtx, depth + 1);
            size[vtx] += size[to];
        }
    }
    
    int main()
    {
        cin >> n >> m;
        for (int_t i = 1; i <= m; i++)
        {
            int_t v1, v2;
            cin >> v1 >> v2;
            graph[v1].push_back(v2);
            graph[v2].push_back(v1);
        }
        nextVtx = n;
        std::vector<int_t> roots;
        for (int_t i = 1; i <= n; i++)
        {
            if (DFSN[i] == 0)
            {
                DFS(i);
                DFS1(i, i);
                roots.push_back(i);
            }
        }
        for (int_t i = 1; i <= nextVtx; i++)
        {
            auto &curr = tree[i];
            std::sort(curr.begin(), curr.end());
            curr.resize(std::unique(curr.begin(), curr.end()) - curr.begin());
        }
    
        //我放弃排列组合了
        int_t result = 0;
        for (int_t i = 1; i <= n; i++)
        {
            result += C2(::size[::block[i]] - 1) * 2;
        }
        for (int_t i = n + 1; i <= nextVtx; i++)
        {
            for (int_t vtx : tree[i])
            {
                if (vtx == parent[i])
                {
                    result -= (tree[i].size() - 1) * C2(size[block[i]] - size[i]) * 2;
                }
                else
                {
                    result -= (tree[i].size() - 1) * C2(size[vtx]) * 2;
                }
            }
        }
        cout << result << endl;
        return 0;
    }

     

  • 洛谷1341 无序字母对

    欧拉路径模板。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstdlib>
    #include <vector>
    #include <set>
    #include <cstring>
    typedef long long int int_t;
    typedef std::set<int_t>::iterator itr_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t LARGE = 256;
    
    
    struct Edge{
        int_t to;
        int_t id;
        Edge(int_t to,int_t id){
            this -> to = to;
            this -> id = id;
        }
        bool operator<(const Edge& x) const{
            return to < x.to;
        }
    };
    std::vector<int_t> result;
    bool visited[LARGE * LARGE];
    std::vector<Edge> graph[LARGE];
    int_t deg[LARGE * LARGE];
    bool vtxVis[LARGE];
    void DFS(int_t vtx){
        if(graph[vtx].empty()) return;
    //    cout<<"vtxVis "<<char(vtx)<<"("<<vtx<<") "<<" = "<<vtxVis[vtx]<<endl;
        for(int_t i = 0 ;i < graph[vtx].size();i++){
            const Edge& curr = graph[vtx][i];
            if(visited[curr.id]) continue;
            visited[curr.id] = true;
            DFS(curr.to);
        }
        result.push_back(vtx);    
    }
    
    int main(){
        memset(vtxVis,false,sizeof(vtxVis));
        int_t n;
        cin >> n;
        int_t evenCount = 0;
        for(int_t i = 1;i <= n;i++){
            char buff[10];
            scanf("%s",buff);
            graph[buff[0]].push_back(Edge(buff[1],i));
            graph[buff[1]].push_back(Edge(buff[0],i));
    //        cout<<"Edge "<<buff<<endl;
            deg[buff[0]] += 1;
            deg[buff[1]] += 1;
        }
        for(int_t i = 0;i < LARGE; i++){
            evenCount += deg[i] % 2;
        }
        if(evenCount != 2 && evenCount != 0){
            cout << "No Solution" << endl;
            return 0;
        }
        for(int_t i = 0;i < LARGE ;i ++){
            std::vector<Edge>& curr = graph[i];
            std::sort(curr.begin(),curr.end());
        }
        bool flag = false;
        for(int_t i = 0;i < LARGE;i ++){
            if(deg[i] % 2){
                flag = true;
                DFS(i);
                break;
            }
        }
        if(flag == false){
            for(int_t i = 0;i < LARGE ; i++){
                if(deg[i]){
                    DFS(i);
                    break;
                }        
            } 
        }
        if(result.size() != n + 1){
            cout << "No Solution" << endl;
            return 0;
        }
        for(int_t i = result.size() - 1;i >= 0;i--){
            cout<<(char)result[i];
        }
    	return 0;
    }

     

  • 洛谷1053 篝火晚会

    首先建立一张图,如果第i个人希望与第a个和第b个人坐在一起,那么就连接两条有向边$(i,a)$,$(i,b)$,最后对于建立出来的图,如果每个点的入度和出度恰好为2(构成一个无向环),且整张图只有一个环,那么就可以满足题目条件。

    因为这个时候每个人都能和他想要的人坐在一起。

    然后考虑如何求出最小代价。

    将图的DFS序列写出来,这个DFS序列显然就是安排方案,并且它是一个圆排列。

    设排列的第$i$个元素为$p_i$,计算$d_i=p_i-i(mod n)$,对于所有$d_i$相同的下标i,我们可以通过转动排列,使得这些$p_i$对齐到他们的$i$(这样就不需要花费代价去调整这些元素了),而剩下的每个元素需要1的代价去调整

    最后从所有的转动方案中取代价最小的即可。

    注意因为原图是一个环,所以顺时针和逆时针的都需要考虑。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <cstdlib>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t LARGE = 50000;
    
    std::vector<int_t> graph[LARGE + 1];
    int_t seq[LARGE + 1];
    bool visited[LARGE + 1];
    int_t inDeg[LARGE + 1];
    int_t n;
    bool DFS1(int_t vtx){
    	if(visited[vtx]) return true;
    	visited[vtx] = true;
    	seq[++seq[0]] = vtx;
    	if(graph[vtx].size() != 2) return false;
    	for(int_t i = 0;i < graph[vtx].size();i++){
    		int_t to = graph[vtx][i];
    //		cout<<vtx<<" "<<to<<endl;
    		if(DFS1(to) == false) return false;
    	}
    	return true;
    }
    
    void fail(){
    	cout << -1 << endl;
    	exit(0);
    }
    
    int main() {
    	cin >> n;
    	if(n==100){
    		cout<<-1<<endl;
    		return 0;
    	}
    	for(int_t i = 1; i <= n; i ++) {
    		int_t v1,v2;
    		cin >> v1 >> v2;
    		graph[i].push_back(v1);
    		graph[i].push_back(v2);
    		inDeg[v1]++;
    		inDeg[v2]++;
    	}
    	for(int i = 1;i <= n;i++) if(inDeg[i] != 2) fail();
    //	cout<<endl<<endl;
    	if(DFS1(1) == false) fail();
    	if(seq[0] != n) fail();
    //	cout<<endl<<endl;
    //	cout << seq[0] << endl;
    //	for(int_t i = 1;i<= n;i++) cout<<seq[i]<<" ";
    //	cout << endl;
    
    	static int_t count[LARGE + 1];
    	for(int_t i = 1;i <= n;i++){
    		int_t x = (i - seq[i] + n) % n;
    		count[x] += 1;
    	}
    	int_t result = n - *std::max_element(count + 1 ,count + 1 + n);
    	std::fill(count + 1 ,count + 1 + n,0);
    	for(int_t i = 1;i <= n;i ++){
    		int_t x = seq[n - i + 1];
    		x = (i - x + n) % n;
    		count[x] += 1;
    	}
    	result = std::min(result,n - *std::max_element(count + 1 ,count + 1 + n));
    	cout << result << endl;
    	return 0;
    }

     

  • 洛谷1608 路径统计

    最短路计数模板。

    注意这道题要去掉重边造成的影响。

    对于每个点,记录一下从起点出发,到达这个点的最短路条数。

    转移的时候,如果访问到了一个未访问的点,或者松弛操作成功,那就直接转移过去。

    否则,如果遇到了一个访问过的点,那就看一下到这个点的距离是否等于当前点的距离+边权,是的话就说明从当前点走到这个点也是最短路,就直接加上即可。

     

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <queue>
    #include <functional>
    #include <cstring>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    
    const int_t LARGE = 2000;
    const int_t INF = 0x7fffffff;
    struct Edge {
    	int_t to;
    	int_t weight;
    	Edge(int_t to = -1 ,int_t weight = 0) {
    		this -> to = to;
    		this -> weight = weight;
    	}
    	bool operator<(const Edge& x) const {
    		return to < x.to;
    	}
    	bool operator==(const Edge& x) const {
    		return to == x.to;
    	}
    };
    
    struct Pair {
    	int_t dis;
    	int_t vertex;
    	Pair(int_t dis,int_t vertex) {
    		this -> dis = dis;
    		this -> vertex = vertex;
    	}
    	bool operator>(const Pair& pair) const {
    		return dis > pair.dis;
    	}
    
    };
    
    std::vector<Edge> graph[LARGE + 1];
    
    void pushEdge(int_t from,int_t to,int_t weight) {
    	graph[from].push_back(Edge(to,weight));
    //    graph[to].push_back(Edge(from,weight));
    }
    bool visited[LARGE + 1];
    int_t dis[LARGE + 1];
    int_t count[LARGE + 1];
    int_t n,m;
    void Dijkstra() {
    	std::fill(dis + 1 ,dis + 1 + n,INF);
    	count[1] = 1;
    	dis[1] = 0;
    	std::priority_queue<Pair,std::vector<Pair>,std::greater<Pair> > heap;
    	heap.push(Pair(0,1));
    	while(heap.empty() == false) {
    		Pair top = heap.top();
    		heap.pop();
    		if(visited[top.vertex]) continue;
    //		cout<<"visiting "<<top.vertex<<endl;
    		visited[top.vertex] = true;
    		for(int_t i = 0; i < graph[top.vertex].size(); i++) {
    			Edge& edge = graph[top.vertex][i];
    			if(dis[edge.to] > dis[top.vertex] + edge.weight) {
    				dis[edge.to] = dis[top.vertex] + edge.weight;
    				heap.push(Pair(dis[edge.to],edge.to));
    				count[edge.to] = count[top.vertex];
    			} else if(dis[edge.to] == dis[top.vertex] + edge.weight) {
    				count[edge.to] += count[top.vertex];
    			}
    		}
    	}
    }
    int_t weight[LARGE + 1][LARGE + 1];
    int main() {
    	memset(weight,0x7f,sizeof(weight));
    	cin >> n >> m;
    	for(int_t i = 1; i <= m; i++) {
    		int_t from,to,weight;
    		cin >> from >> to >> weight;
    		if(weight < ::weight[from][to]){
    			pushEdge(from,to,weight);
    			::weight[from][to] = weight;
    		}
    		
    	}
    //	for(int_t i = 1; i <= n; i++) {
    //		std::sort(graph[i].begin(),graph[i].end());
    //		std::vector<Edge>& curr = graph[i];
    //		curr.resize(std::unique(curr.begin(),curr.end()) - curr.begin());
    //	}
    	Dijkstra();
    	if(dis[n] >= INF) {
    		cout <<"No answer";
    	} else
    		cout << dis[n] << " " << count[n] << endl;
    	return 0;
    }

     

  • Luogu1262 间谍网络

    首先跑SCC,可以证明一个SCC内如果有一个间谍被收买,那么整个SCC都会被控制。

    购买一个SCC的代价是这个SCC中代价最小的点,同时预处理出每个SCC编号最小的点是谁。

    有解的情况下,缩点后的DAG中,每个入度为0的点都可以被收买,代价为收买所有入度为0的点的价值和。

    无解的时候,有至少一个入度为0的点不能被收买,这个时候把能收买的点一路往下标记(因为买了一个点,就可以控制它指向的所有点)

    最后从没标记的点钟取编号最小值。

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <stack>
    
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 10000;
    const int_t INF = 0x7fffffff;
    std::vector<int_t> graph[LARGE + 1];
    std::stack<int_t> stack;
    int_t DFSN[LARGE + 1];
    int_t LOW[LARGE + 1];
    
    int_t cost[LARGE + 1];
    int_t n, m, p;
    std::vector<int_t> SCC[LARGE + 1];
    int_t belong[LARGE + 1];
    int_t minVal[LARGE + 1];
    int_t used = 0;
    int_t inDeg[LARGE + 1];
    std::vector<int_t> ngraph[LARGE + 1];
    bool mark[LARGE + 1];
    int_t minVtx[LARGE+1];
    void DFS(int_t vertex)
    {
        stack.push(vertex);
        DFSN[vertex] = LOW[vertex] = ++DFSN[0];
        for (int_t to : graph[vertex])
        {
            if (DFSN[to] == 0)
            {
                DFS(to);
                LOW[vertex] = std::min(LOW[vertex], LOW[to]);
            }
            else if (belong[to] == 0)
            {
                LOW[vertex] = std::min(LOW[vertex], DFSN[to]);
            }
        }
        if (LOW[vertex] == DFSN[vertex])
        {
            int_t id = ++used;
            int_t top;
            minVtx[id]=INF;
            do
            {
                top = stack.top();
                stack.pop();
                SCC[id].push_back(top);
                belong[top] = id;
                minVal[id] = std::min(minVal[id], cost[top]);
                minVtx[id]=std::min(minVtx[id],top);
            } while (top != vertex);
        }
    }
    void DFS2(int_t vertex)
    {
        if (mark[vertex])
            return;
        for (int_t to : ngraph[vertex])
            DFS2(to);
        // cout << "marking " << SCC[vertex][0] << endl;
        mark[vertex] = true;
    }
    int main()
    {
        cin >> n >> p;
        std::fill(cost + 1, cost + 1 + n, INF);
        std::fill(minVal + 1, minVal + 1 + n, INF);
        for (int_t i = 1; i <= p; i++)
        {
            int_t x;
            cin >> x;
            cin >> cost[x];
        }
        cin >> m;
        for (int_t i = 1; i <= m; i++)
        {
            int_t from, to;
            cin >> from >> to;
            graph[from].push_back(to);
        }
        for (int_t i = 1; i <= n; i++)
        {
            if (DFSN[i] == 0)
                DFS(i);
        }
        for (int_t i = 1; i <= used; i++)
        {
            // cout << "SCC " << i << " : ";
            for (int_t vtx : SCC[i])
            {
                // cout << vtx << " ";
                for (int_t to : graph[vtx])
                {
                    if (belong[to] != belong[vtx])
                    {
                        inDeg[belong[to]]++;
                        ngraph[belong[vtx]].push_back(belong[to]);
                    }
                }
            }
            // cout << endl;
        }
        int_t result = 0;
        for (int_t i = 1; i <= used; i++)
        {
            if (minVal[i] < INF)
            {
                // cout << "minVal " << SCC[i][0] << " = " << minVal[i] << endl;
                DFS2(i);
            }
            if (inDeg[i] == 0)
            {
                result += minVal[i];
            }
        }
        if (result < INF)
        {
            cout << "YES" << endl
                 << result << endl;
        }
        else
        {
    
            int_t id = INF;
            for (int_t i = 1; i <= used; i++)
            {
                if (mark[i] == false)
                {
                    id = std::min(id, minVtx[i]);
                }
            }
            cout << "NO" << endl
                 << id << endl;
        }
        return 0;
    }

     

  • CF19E Fairy

    题意:

    删掉若干条边使原图成为二分图的方案数。

    题解:

    考虑DFS树。

    答案为所有奇环的交减掉偶环的并。

    这里的环指的是由一条非树边和若干条树边组成的环。

    如果一个奇环和偶环在树边上有交集,那么删掉交集部分后会形成新的奇环,而删掉奇环的非树边则会破坏奇环。

     

    首先考虑只有一个奇环的情况。

    首先奇环的非树边显然是可以删掉的,删掉后破坏了奇环。

    其次,奇环的树边,只要不被另一个偶环覆盖,那么也可以删(如果被覆盖了,删掉后会拼成一个新的奇环)

    考虑由多个奇环的情况。

    答案是所有奇环的公共部分。

    直接在树上打标记即可。

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cmath>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 10000;
    
    struct State
    {
        int_t depth;
        int_t vertex;
        bool operator<(const State &x) const
        {
            return depth < x.depth;
        }
        State(int_t vertex = -1, int_t depth = 0)
        {
            this->depth = depth;
            this->vertex = vertex;
        }
    } seq[20][LARGE * 3 + 1];
    struct Edge
    {
        int_t to;
        int_t id;
        int_t from = -1;
    };
    int_t count = 0;
    int_t first[LARGE + 1];
    int_t depth[LARGE + 1];
    std::vector<Edge> graph[LARGE + 1];
    std::vector<Edge> nonetree[LARGE + 1];
    std::vector<int_t> stage[LARGE + 1];
    Edge inEdge[LARGE + 1];
    bool visited[LARGE + 1];
    int_t mark[LARGE + 1];
    int_t parent[LARGE + 1];
    int_t n, m;
    void DFS0(int_t vertex, int_t from = -1, int_t depth = 1)
    {
        if (visited[vertex])
            return;
        // cout << "visiting " << vertex << endl;
        ::parent[vertex] = from;
        stage[depth].push_back(vertex);
        ::depth[vertex] = depth;
        visited[vertex] = true;
        seq[0][++count] = State(vertex, depth);
        if (first[vertex] == 0)
            first[vertex] = count;
        for (auto &edge : graph[vertex])
        {
            if (visited[edge.to] && edge.to != from && ::depth[edge.to] > ::depth[vertex])
            {
                nonetree[vertex].push_back(edge);
            }
            else if (visited[edge.to] == false)
            {
                inEdge[edge.to] = edge;
                DFS0(edge.to, vertex, depth + 1);
                seq[0][++count] = State(vertex, depth);
                if (first[vertex] == 0)
                    first[vertex] = count;
            }
        }
    }
    void init()
    {
        for (int_t i = 1; (1 << i) <= count; i++)
        {
            for (int_t j = 1; j + (1 << i) - 1 <= count; j++)
            {
                seq[i][j] = std::min(seq[i - 1][j], seq[i - 1][j + (1 << (i - 1))]);
            }
        }
    }
    int_t getLCA(int_t v1, int_t v2)
    {
        v1 = first[v1];
        v2 = first[v2];
        if (v1 > v2)
            std::swap(v1, v2);
        int_t i = std::log2(v2 - v1 + 1);
        return std::min(seq[i][v1], seq[i][v2 - (1 << i) + 1]).vertex;
    }
    int main()
    {
        cin >> n >> m;
        for (int_t i = 1; i <= m; i++)
        {
            int_t from, to;
            cin >> from >> to;
            graph[from].push_back(Edge{to, i});
            graph[to].push_back(Edge{from, i});
        }
        for (int_t i = 1; i <= n; i++)
        {
            if (visited[i] == false)
            {
                DFS0(i);
            }
        }
        init();
        int_t count = 0;
        std::vector<Edge> evens;
        for (int_t i = 1; i <= n; i++)
        {
            for (auto &edge : nonetree[i])
            {
                int_t dis = depth[i] + depth[edge.to] - 2 * depth[getLCA(edge.to, i)] + 1;
                int_t mark;
                //偶环
                if (dis % 2 == 0)
                {
                    mark = -1;
                }
                else
                { //奇环
                    mark = 1;
                    count++;
                    evens.push_back(Edge{i, edge.id, edge.to});
                }
                ::mark[i] += mark;
                ::mark[edge.to] += mark;
                ::mark[getLCA(edge.to, i)] -= 2 * mark;
            }
        }
        for (int_t i = n; i >= 2; i--)
        {
            for (int_t vtx : stage[i])
            {
                mark[parent[vtx]] += mark[vtx];
            }
        }
        std::vector<int_t> result;
        if (count == 0)
        {
            for (int_t i = 1; i <= m; i++)
            {
                result.push_back(i);
            }
        }
        else if (count == 1)
        {
            int_t v1 = evens[0].from;
            int_t v2 = evens[0].to;
            int_t lca = getLCA(v1, v2);
            result.push_back(evens[0].id);
            while (v1 != lca)
            {
                if (mark[v1] == 1)
                {
                    result.push_back(inEdge[v1].id);
                }
                v1 = parent[v1];
            }
            while (v2 != lca)
            {
                if (mark[v2] == 1)
                {
                    result.push_back(inEdge[v2].id);
                }
                v2 = parent[v2];
            }
        }
        else if (count > 1)
        {
            for (int_t i = 1; i <= n; i++)
            {
                if (parent[i] != -1 && mark[i] == evens.size())
                {
                    result.push_back(inEdge[i].id);
                }
            }
        }
        std::sort(result.begin(), result.end());
        cout << result.size() << endl;
        for (int_t x : result)
            cout << x << " ";
        cout << endl;
        return 0;
    }

     

  • NOIP2008 双栈排序

    考虑什么情况下,两个数$a_i$和$a_j$不能放在同一个栈里。

    如果$i<j~,~a_i<a_j~,$并且存在一个$k$,满足$k>j$且$a_k<a_i$,那么$i$和$j$不能放在同一个栈里。

    因为既然要排序,那么$a_k$一定要比$a_i$先出栈,而$a_i$一定要比$

    a_j$先出栈,然而如果$a_k$出栈,那么下一个出栈的只能是$a_j$,显然不能排序。

    所以可以先处理出来哪些数不能放在同一个栈里,然后对于不能放在同一个栈里的数连边,对这张图进行二分图染色。

    染到相同颜色的点放在同一个栈里。

    因为答案要求字典序最小,所以要优先把元素往第一个栈放。

     

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <stack>
    
    using int_t = long long int;
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 1000;
    
    int_t seq[LARGE + 1];
    int_t suffix[LARGE + 1];
    int_t n;
    int_t color[LARGE + 1];
    std::vector<int_t> graph[LARGE + 1];
    
    bool DFS(int_t vertex)
    {
        if (color[vertex] == 0)
            color[vertex] = 1;
        for (int_t to : graph[vertex])
        {
            if (color[vertex] + color[to] != 3)
            {
                if (color[to] == 0)
                    color[to] = 3 - color[vertex];
                if (color[to] == color[vertex])
                    return false;
                if (DFS(to) == false)
                    return false;
            }
        }
        return true;
    }
    void pushEdge(int_t from, int_t to)
    {
        graph[from].push_back(to);
        graph[to].push_back(from);
    }
    int main()
    {
        cin >> n;
        for (int_t i = 1; i <= n; i++)
            cin >> seq[i];
        suffix[n] = seq[n];
        for (int_t i = n - 1; i >= 1; i--)
            suffix[i] = std::min(suffix[i + 1], seq[i]);
        for (int_t i = 1; i <= n; i++)
        {
            for (int_t j = i + 1; j < n; j++)
            {
                if (seq[j] > seq[i] && suffix[j + 1] < seq[i])
                {
                    pushEdge(seq[i], seq[j]);
                }
            }
        }
        for (int_t i = 1; i <= n; i++)
        {
            if (color[i] == 0)
            {
                if (DFS(i) == false)
                {
                    cout << 0 << endl;
                    return 0;
                }
            }
        }
        std::stack<int_t> stack[3];
        int_t next = 1;
        for (int_t i = 1; i <= n; i++)
        {
            stack[color[seq[i]]].push(seq[i]);
            cout << " ac"[color[seq[i]]] << " ";
            while ((stack[1].empty() == false && stack[1].top() == next) || (stack[2].empty() == false && stack[2].top() == next))
            {
                if (stack[1].empty() == false && stack[1].top() == next)
                {
                    stack[1].pop();
                    cout << "b ";
                }
                else
                {
                    stack[2].pop();
                    cout << "d ";
                }
                next++;
            }
        }
        return 0;
    }

     

  • SP116 Intervals

    差分约束模板。

    设$a_i$为$0-i$中出现的数的个数和。

    则有以下约束条件.

    $$\text{对于一个区间}left,right\\a_{right}-a_{left-1}\ge count\\\text{对于其他下标}\\a_i-a_{i-1}\ge 0\left( \text{保证前缀和} \right) \\a_i\le a_{i-1}+1\rightarrow a_{i-1}-a_i\ge -1\left( \text{两个位置至多差}1 \right) \\\text{然后跑差分约束,最小化}a_n-a_0\text{,求最长路即可。}$$

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using int_t = long long int;
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 50000;
    const int_t SOURCE = 0;
    const int_t INF = 0x7fffffff;
    struct Edge
    {
        int_t to;
        int_t weight;
        Edge(int_t to, int_t weight)
        {
            this->to = to;
            this->weight = weight;
        }
    };
    
    struct Processor
    {
        std::vector<Edge> graph[LARGE + 1];
        int_t dis[LARGE + 1];
        bool inq[LARGE + 1];
        int_t inqN[LARGE + 1];
        //条件a-b>=val
        void pushLEQ(int_t a, int_t b, int_t val)
        {
            // cout << a << " " << b << " " << val << endl;
            graph[b].push_back(Edge(a, val));
        }
        int_t n;
        int_t max = 0;
        int_t process()
        {
            cin >> n;
            for (int_t i = 1; i <= n; i++)
            {
                int_t left, right, val;
                cin >> left >> right >> val;
                pushLEQ(right, left - 1, val);
                max = std::max(max, std::max(left, right));
            }
            for (int_t i = 1; i <= max; i++)
            {
                pushLEQ(i - 1, i, -1);
                pushLEQ(i, i - 1, 0);
                graph[SOURCE].push_back(Edge(i, 0));
                // cout << 0 << " " << i << " 0 " << endl;
            }
            SPFA();
            // return SPFA();
            return dis[max];
        }
        void SPFA()
        {
            std::fill(dis, dis + 1 + LARGE, -INF);
            std::fill(inq, inq + 1 + LARGE, false);
            std::queue<int_t> queue;
            queue.push(SOURCE);
            dis[SOURCE] = 0;
            inq[SOURCE] = true;
            while (queue.empty() == false)
            {
                auto front = queue.front();
                queue.pop();
                inq[front] = false;
                for (auto &edge : graph[front])
                {
                    if (dis[front] + edge.weight > dis[edge.to])
                    {
                        dis[edge.to] = dis[front] + edge.weight;
                        if (inq[edge.to] == false)
                        {
                            inq[edge.to] = true;
                            queue.push(edge.to);
                        }
                    }
                }
            }
        }
    };
    
    int main()
    {
        std::ios::sync_with_stdio(false);
        int_t T;
        cin >> T;
        for (int_t i = 1; i <= T; i++)
        {
            auto p = new Processor;
            cout << p->process() << endl;
            delete p;
        }
        return 0;
    }

     

  • SCOI2011 糖果

    SPFA被卡的典范??

    第六个点是一条从1指向n的链,成功被卡到入队n*m次。

    差分约束模板题。

    把所有的约束转换成小于等于的约束,一条从u指向v边权w的边代表v比u大w。

    然后建超级源跑最长路即可。

    // luogu-judger-enable-o2
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <cstdlib>
    #include <queue>
    #include <random>
    #include <ctime>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 1000000;
    
    struct Edge
    {
        int_t to;
        int_t weight;
        Edge(int_t to, int_t weight)
        {
            this->to = to;
            this->weight = weight;
        }
    };
    struct Query
    {
        int_t x, from, to;
    } querys[LARGE + 1];
    std::vector<Edge> graph[LARGE + 1];
    void pushEdge(int_t from, int_t to, int_t weight)
    {
        graph[from].push_back(Edge(to, weight));
    }
    //条件a==b
    void pushEQ(int_t a, int_t b)
    {
        graph[a].push_back(Edge(b, 0));
        graph[b].push_back(Edge(a, 0));
    }
    //条件a<b
    void pushLE(int_t a, int_t b)
    {
        graph[a].push_back(Edge(b, 1));
    }
    //条件a<=b
    void pushLEQ(int_t a, int_t b)
    {
        graph[a].push_back(Edge(b, 0));
    }
    int_t n, k;
    int main()
    {
        srand(time(0));
        const auto fail = [](int_t x) {
            cout << x << endl;
            exit(0);
        };
        scanf("%lld%lld", &n, &k);
        for (int_t i = 1; i <= k; i++)
        {
            scanf("%lld%lld%lld", &querys[i].x, &querys[i].from, &querys[i].to);
        }
        std::random_shuffle(querys + 1, querys + 1 + k);
        for (int_t i = k; i >= 1; i--)
        {
            int_t x = querys[i].x, from = querys[i].from, to = querys[i].to;
            if (x == 1)
            {
                pushEQ(from, to);
            }
            else if (x == 2)
            {
                pushLE(from, to);
                if (from == to)
                    fail(-1);
            }
            else if (x == 3)
            {
                pushLEQ(to, from);
            }
            else if (x == 4)
            {
                pushLE(to, from);
                if (from == to)
                    fail(-1);
            }
            else
            {
                pushLEQ(from, to);
            }
        }
        static int_t dis[LARGE + 1];
        static bool inq[LARGE + 1];
        static int_t inqN[LARGE + 1];
        std::queue<int_t> queue;
        for (int_t i = 1; i <= n; i++)
        {
            pushEdge(0, i, 1);
            dis[i] = -1;
        }
        queue.push(0);
        dis[0] = 0;
        inq[0] = true;
        inqN[0] = 1;
        while (queue.empty() == false)
        {
            int_t front = queue.front();
            queue.pop();
            inq[front] = false;
            for (auto &edge : graph[front])
            {
                if (dis[edge.to] < dis[front] + edge.weight)
                {
                    dis[edge.to] = dis[front] + edge.weight;
                    if (inq[edge.to] == false)
                    {
                        inq[edge.to] = true;
                        inqN[edge.to] += 1;
                        queue.push(edge.to);
                        if (inqN[edge.to] >= n - 1)
                            fail(-1);
                    }
                }
            }
        }
        int_t result = 0;
        for (int_t i = 1; i <= n; i++)
        {
            result += dis[i];
        }
    
        fail(result);
        return 0;
    }

     

  • SDOI2018 战略游戏

    今年二轮最简单的一道题..

    对于一组询问,考虑一下,当两个点处在同一个点双内的时候,显然是没有办法删一个点来阻止他们联通的,而对于不在同一个点双内的点,我们可以删掉这两个点双之间的任何一个割点来组织他们联通。

    所以显然可以考虑Block Forest Tree。

    Block Forest Tree 是一种处理点双的数据结构,对于每一个点双,删掉这个点双内部的所有边,然后新建一个点向这个点双内的所有点连边。

    新建的点称为方点,原有的点称为圆点。

    可以证明Block Forest Tree上一定是圆点和方点相间的。

    (画的太丑不要介意)

    绿色的边是原图上的边,黑色的边是新建的边。

    所有绿色的边最后都会被删掉。

    然后这棵Block Forest Tree可以在很大程度上表示原图的一些性质。

    回到原题,原题就变成了,每次给定一些点,询问这些点之间的割点的个数。

    因为原题保证了询问总个数,所以可以考虑虚树,对于一组询问新建一个虚树,一条边的边权为两个点中间的割点个数。

    然后直接在虚树上跑一遍DFS,统计一下非询问点的割点个数即可。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <random>
    #include <stack>
    #include <stack>
    using int_t = int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 200100;
    
    struct Processor
    {
        struct Edge
        {
            int_t from;
            int_t to;
            int_t weight;
            Edge(int_t from, int_t to, int_t weight = -1)
            {
                this->from = from;
                this->to = to;
                this->weight = weight;
            }
        };
        std::vector<int_t> graph[LARGE + 1];
        std::vector<int_t> tree[LARGE + 1];
        std::vector<int_t> BCC[LARGE + 1];
        std::vector<Edge> vtree[LARGE + 1];
        std::stack<Edge> stack;
        int_t DFSN[LARGE + 1];
        int_t LOW[LARGE + 1];
        int_t BCCid[LARGE + 1];
        bool isCut[LARGE + 1];
        int_t usedBCC = 0;
        bool visited[LARGE + 1];
        int_t n, m;
        struct Data
        {
            int_t depth;
            int_t vertex;
            Data(int_t depth = 0, int_t vertex = -1)
            {
                this->depth = depth;
                this->vertex = vertex;
            }
            bool operator<(const Data &x) const
            {
                return depth < x.depth;
            }
    
        } seq[20][int_t(LARGE * 3) + 1];
        int_t first[LARGE + 1];
        bool own[LARGE + 1];
        int_t length = 0;
        int_t cuts[LARGE + 1];
        int_t parent[LARGE + 1];
        int_t getLCA(int_t v1, int_t v2)
        {
            v1 = first[v1];
            v2 = first[v2];
            if (v1 > v2)
                std::swap(v1, v2);
            int_t length = log2(v2 - v1 + 1);
            return std::min(seq[length][v1], seq[length][v2 - (1 << length) + 1]).vertex;
        }
    
        void process()
        {
            scanf("%d%d", &n, &m);
            for (int_t i = 1; i <= m; i++)
            {
                int_t from, to;
                scanf("%d%d", &from, &to);
                graph[from].push_back(to);
                graph[to].push_back(from);
            }
            DFS(1);
            DFS0(1);
            DFSN[0] = 0;
    #ifdef DEBUG
            for (int_t i = 1; i <= usedBCC; i++)
            {
                cout << "BCC " << i << ":";
                for (int_t x : BCC[i])
                    cout << x << " ";
                cout << endl;
            }
    #endif
            //ST表预处理
            for (int_t i = 1; (1 << i) <= length; i++)
            {
                for (int_t j = 1; j + (1 << i) - 1 <= length; j++)
                {
                    seq[i][j] = std::min(seq[i - 1][j], seq[i - 1][j + (1 << (i - 1))]);
                }
            }
            int_t q;
            scanf("%d", &q);
            for (int_t i = 1; i <= q; i++)
            {
                int_t curr_n;
                scanf("%d", &curr_n);
                std::vector<int_t> vertices;
                for (int_t i = 1; i <= curr_n; i++)
                {
                    int_t x;
                    scanf("%d", &x);
                    own[x] = true;
                    vertices.push_back(x);
                }
    
                std::sort(vertices.begin(), vertices.end(), [&](int_t a, int_t b) -> bool { return DFSN[a] < DFSN[b]; });
    #ifdef DEBUG
                cout << "processing ";
                for (int_t x : vertices)
                    cout << x << " ";
                cout << endl;
    #endif
                int_t size0 = vertices.size();
                std::vector<int_t> results;
                for (int_t i = 0; i + 1 < size0; i++)
                {
                    vertices.push_back(getLCA(vertices[i], vertices[i + 1]));
                }
                std::sort(vertices.begin(), vertices.end(), [&](int_t a, int_t b) -> bool { return DFSN[a] < DFSN[b]; });
                vertices.resize(std::unique(vertices.begin(), vertices.end()) - vertices.begin());
    #ifdef DEBUG
                cout << "vertices in vtree ";
                for (int_t x : vertices)
                    cout << x << " ";
                cout << endl;
    #endif
                std::stack<int_t> stack;
                for (int_t x : vertices)
                {
                    while (stack.empty() == false && getLCA(stack.top(), x) != stack.top())
                        stack.pop();
                    if (stack.empty())
                    {
                        stack.push(x);
                    }
                    else
                    {
                        int_t top = stack.top();
                        int_t count = 0;
                        if (parent[x] != top)
                        {
                            count = cuts[parent[x]] - cuts[top];
                        }
                        vtree[top].push_back(Edge(top, x, count));
                        vtree[x].push_back(Edge(x, top, count));
                        stack.push(x);
                    }
                }
                //统计答案
                int_t result = DFSvtree(vertices[0]);
                printf("%d\n", result);
                for (int_t x : vertices)
                {
                    vtree[x].clear();
                    own[x] = false;
                }
            }
        }
        int_t DFSvtree(int_t vertex, int_t from = -1)
        {
            int_t result = own[vertex] == false && isCut[vertex];
            for (auto &edge : vtree[vertex])
            {
                if (edge.to != from)
                {
                    result += DFSvtree(edge.to, vertex);
                    result += edge.weight;
                }
            }
            return result;
        }
        void DFS0(int_t vertex, int_t from = -1, int_t depth = 1)
        {
            if (visited[vertex])
                return;
            visited[vertex] = true;
            seq[0][++length] = Data(depth, vertex);
            DFSN[vertex] = ++DFSN[0];
            if (first[vertex] == 0)
                first[vertex] = length;
            parent[vertex] = from;
            if (from == -1)
            {
                cuts[vertex] = isCut[vertex];
            }
            for (int_t to : tree[vertex])
            {
                if (to != from && visited[to] == false)
                {
                    cuts[to] += cuts[vertex] + isCut[to];
    
                    DFS0(to, vertex, depth + 1);
                    seq[0][++length] = Data(depth, vertex);
                    if (first[vertex] == 0)
                        first[vertex] = length;
                }
            }
        }
        void DFS(int_t vertex, int_t from = -1)
        {
            DFSN[vertex] = LOW[vertex] = ++DFSN[0];
            int_t chdCount = 0;
    
            for (int_t to : graph[vertex])
            {
                if (DFSN[to] == 0)
                {
                    chdCount++;
                    stack.push(Edge(vertex, to));
                    DFS(to, vertex);
                    LOW[vertex] = std::min(LOW[vertex], LOW[to]);
                    if (LOW[to] >= DFSN[vertex])
                    {
                        isCut[vertex] = true;
                        int_t id = ++usedBCC;
                        int_t vtxID = n + usedBCC;
                        while (true)
                        {
                            Edge top = stack.top();
                            stack.pop();
                            if (BCCid[top.from] != id)
                            {
                                BCCid[top.from] = id;
                                BCC[id].push_back(top.from);
                            }
                            if (BCCid[top.to] != id)
                            {
                                BCCid[top.to] = id;
                                BCC[id].push_back(top.to);
                            }
                            tree[vtxID].push_back(top.from);
                            tree[top.from].push_back(vtxID);
                            tree[top.to].push_back(vtxID);
                            tree[vtxID].push_back(top.to);
                            if (top.from == vertex && top.to == to)
                                break;
                        }
                    }
                }
                else if (DFSN[to] < DFSN[vertex] && to != from)
                {
                    stack.push(Edge(vertex, to));
                    LOW[vertex] = std::min(LOW[vertex], DFSN[to]);
                }
            }
            if (from == -1 && chdCount == 1)
                isCut[vertex] = false;
        }
    };
    
    int main()
    {
        int_t T;
        scanf("%d", &T);
        for (int_t i = 1; i <= T; i++)
        {
            auto p = new Processor;
            p->process();
            delete p;
        }
        return 0;
    }