zoukankan      html  css  js  c++  java
  • 省选模拟测试13

    期望得分:(100+40+0 = 140)

    实际得分:(30+40+0=70)

    (T1) 巨型分类讨论的构造题,自己少考虑一种情况,只拿了一个 (subtack) 的分。

    (T2) 比较难的转化题,主要是我不怎么会仙人掌,考试的时候只打了树的暴力分。

    (T3) 题目都没读懂,暴力也就没打明白。

    T1 华灵-蝶妄想 (butterfly)

    题意描述

    给定一个 (n∗m) 大小的网格, 每个格子里可以填 ′(′ 或 ′)′. 某一行或某一列可以形成一个合法的括号序列.问最大有多少行和多少列是合法的括号序列.

    数据范围:(n,mleq 5000)

    solution

    巨型分类讨论构造题。

    首先因为合法的括号序只能为偶数,所以我们要分奇偶讨论一下:

    • (n) 为奇数

    这种情况很简单,只需要让每一列都合法即可,答案为 (m), 构造方案如下:

    //n=3, m = 4 的情况
    (((
    (((
    )))
    )))    
    
    • (m) 为奇数的时候

    和第一种情况一下,只不过我们需要让每一行合法,答案为 (n), 构造方案如下:

    //n = 4, m = 3 的情况
    ()()
    ()()
    ()()
    
    • (n,m) 都为偶数的时候

    这种情况就比较复杂了,我们有三种构造方案。

    方案1: 答案为 (n+m-4)

    我们考虑舍弃第一行和最后一行,中间的行构造:

    //n = 6, m = 6 的情况
    ((((((
    (()())
    ()()()
    (()())
    ()()()
    ))))))
    

    不难发现这样其实是 (n+m-4)

    方案2:答案为 (n/2+m-1)

    (n) 比较小的时候,我们可以考虑牺牲一半的行来这样构造

    // n = 4, m = 6
    ((((((
    ()()()
    )()()(
    ))))))
    

    方案3:答案为:(n+m/2-1)

    这个其实和方案2的思路是一样的,就是把上面那个倒过来一下就可以了。

    //n = 6,, m = 4
    ((((
    ()()
    )()(
    ()()
    )()(
    ))))
    

    所以当 (n,m) 都为偶数的时候,我们取这三种方案的最大值就可以了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m;
    char s[5010][5010];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int main()
    {
    	freopen("butterfly.in","r",stdin);
    	freopen("butterfly.out","w",stdout);
        n = read(); m = read();
        if((n & 1) && (m & 1))
        {
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                {
                    cout<<'(';
                }
                cout<<endl;
            }
        }
        else if(n & 1)
        {
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m/2; j++) cout<<'(';
                for(int j = m/2+1; j <= m; j++) cout<<')';
                cout<<endl;
            }
        }
        else if(m & 1)
        {
            for(int i = 1; i <= m; i++)
            {
                for(int j = 1; j <= n/2; j++) s[j][i] = '(';
                for(int j = n/2+1; j <= n; j++) s[j][i] = ')';
            }
            for(int i = 1; i <= n; i++)
            {
                for(int j = 1; j <= m; j++)
                {
                    cout<<s[i][j];
                }
                cout<<endl;
            }
        }
        else
        {
        	int t1 = n+m-4, t2 = n/2+m-1, t3 = n+m/2-1;
        	int maxn = max(t1,max(t2,t3));
        	if(maxn == t1)
        	{
        		for(int i = 1; i <= m; i++) cout<<'(';
        		cout<<endl;
        		for(int i = 2; i <= n-1; i++)
        		{
        			if(i&1) for(int j = 1; j <= m; j++) (j & 1) ? cout<<'(' : cout<<')';
        			else
    			{
    				cout<<'('; 
    				for(int j = 2; j <= m-1; j++) !(j & 1) ? cout<<'(' : cout<<')'; 
    				cout<<')';		
                                    cout<<endl;
    			}  
    			for(int i = 1; i <= m; i++) cout<<')';
    			cout<<endl;
    		}
    		else if(maxn == t2)
    		{
    			for(int i = 1; i <= m; i++) cout<<'(';
    			cout<<endl;
    			for(int i = 2; i <= n-1; i++)
    			{
    				if(i & 1) for(int j = 1; j <= m; j++) (j&1) ? cout<<'(' : cout<<')';
    				else for(int j = 1; j <= m; j++) !(j&1) ? cout<<'(' : cout<<')';
    				cout<<endl;
    			}
    			for(int i = 1; i <= m; i++) cout<<')';
    			cout<<endl;
    		}
    		else if(maxn == t3)
    		{
    			for(int i = 1; i <= n; i++) s[i][1] = '(';
    			for(int i = 1; i <= n; i++) s[i][m] = ')';
    			for(int i = 2; i <= m-1; i++)
    			{
    				if((i & 1) == 0) for(int j = 1; j <= n; j++) s[j][i] = (j&1) ? ')' : '(';
    				else for(int j = 1; j <= n; j++) s[j][i] = (j&1) ? '(' : ')';
    			}
    			for(int i = 1; i <= n; i++)
    			{
    				for(int j = 1; j <= m; j++) cout<<s[i][j];
    				cout<<endl;
    			}
    		} 
        }
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T2 樱符-完全墨染的樱花 (sakura)

    题意描述

    给你一个 (n) 个点 (m) 条边的无向图,定义一个图的价值为 (displaystylesum_{s=1}^{n}sum_{t=1}^{n} maxflow(s,t) imes p^{(s-1) imes n+t})(maxflow(s,t)) 即为 (s,t) 之间的最大流。

    一开始图上的边权全为 (1), 且 (maxflow(s,t)leq 2), 现在将第 (i) 条边的边权改为了 (c[i]), 问你修改后图的价值最大是多少。

    数据范围:(nleq 3 imes 10^5,mleq 5 imes 10^5)

    solution

    并查集加构造转化

    原图其实有个特殊性质 (maxflow(s,t)leq 2) ,这就表示一条边只会在一个环上。

    因为如果一条边在两个环上的话,显然最大流为 (3)

    也就表明这个图其实就是个仙人掌。

    考虑这个仙人掌图上的最小割(最小割等于最大流)。

    割边要么是一条桥边,要么是同时割掉环上的两条边。考虑当割掉环上的两条边的时候,无论怎么样边权最小的肯定会被割掉。

    因此我们可以找到每个环上边权最小的边,然后把环上的其他边的边权加上边权最小的边的边权,然后在把边权最小的边断掉,不难发现这样我们这样构造出来的最大流和原图的最大流是一样的。

    由于我们把每个环都断掉了一条边,显然构造出来的图是一棵树。

    我们考虑把树上的每一条边从大到小排序,依次枚举每一条边,合并左右端点所在的联通块,不难发现从左端点所在的联通块中选择一个起点,在从右端点所在联通块中选一个终点(反过来也可以),最大流一定是当前枚举这条边的边权。因此可以那并查集来维护一下每个联通块的 (sum p^{{(i-1}) imes n})(sum p^{i}) 即可。

    感觉这个题难点就在这个转化上,树的部分其实很好想的。

    一个小技巧:仙人掌图找环的时候,可以先建一个树出来,然后枚举所有非树边,边的两个端点树上路径的边就是环上的其他边。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    #define LL long long
    const int inf = 1e9+10;
    const int mod = 998244353;
    const int N = 3e5+10;
    int n,m,p,s,t,u,v,w,tot = 1;
    int head[N],dep[N],fa[N],siz[N],f[N],id[N];
    LL sum1[N],sum2[N];
    bool can[N],used[N];
    struct bian
    {
    	int u,v,w,tag;
    }q[N<<1];
    struct node
    {
    	int to,net,w,id;
    }e[N<<1];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    bool comp(bian a,bian b){return a.w > b.w;}
    void add(int x,int y,int id)
    {
    	e[++tot].to = y;
    	e[tot].id = id;
    	e[tot].net = head[x];
    	head[x] = tot;
    }
    LL ksm(LL a,LL b)
    {
    	LL res = 1;
    	for(; b; b >>= 1)
    	{
    		if(b & 1) res = 1LL * res * a % mod;
    		a = 1LL * a * a % mod;
    	}
    	return res;
    }
    int find(int x)
    {
    	if(fa[x] == x) return x;
    	else return fa[x] = find(fa[x]);
    }
    void slove()
    {
    	LL ans = 0, tmp = ksm(p,n);
    	sort(q+1,q+m+1,comp);
    	fa[1] = 1; sum1[1] = 1; sum2[1] = p;
    	for(int i = 2; i <= n; i++)
    	{
    		fa[i] = i;
    		sum1[i] = 1LL * sum1[i-1] * tmp % mod;
    		sum2[i] = 1LL * sum2[i-1] * p % mod;
    	}
    	for(int i = 1; i <= m; i++)
    	{
    		int fx = find(q[i].u), fy = find(q[i].v);
    		if(fx == fy || q[i].tag) continue;
    		ans = (ans + 1LL * q[i].w * sum1[fx] % mod * sum2[fy] % mod) % mod;
    		ans = (ans + 1LL * q[i].w * sum1[fy] % mod * sum2[fx] % mod) % mod;
    		fa[fx] = fy;
    		sum1[fy] = (sum1[fy] + sum1[fx]) % mod;
    		sum2[fy] = (sum2[fy] + sum2[fx]) % mod;
    	}
    	printf("%lld
    ",ans);
    }
    void dfs(int x,int fa)
    {
    	f[x] = fa; dep[x] = dep[fa] + 1;
    	for(int i = head[x]; i; i = e[i].net)
    	{
    		int to = e[i].to;
    		if(to == fa) continue;
    		id[to] = e[i].id;
    		dfs(to,x);
    	}
    }
    int main()
    {
    	freopen("sakura.in","r",stdin);
    	freopen("sakura.out","w",stdout);
    	n = read(); m = read(); p = read();
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	for(int i = 1; i <= m; i++)
    	{
    		q[i].u = read(), q[i].v = read(), q[i].w = read();
    		int fx = find(q[i].u), fy = find(q[i].v);
    		if(fx == fy) continue;
    		fa[fx] = fy; used[i] = 1;
    		add(q[i].u,q[i].v,i); add(q[i].v,q[i].u,i);
    	}
    	dfs(1,0);
    	for(int i = 1; i <= m; i++)
    	{
    		if(!used[i])
    		{
    		        int x = q[i].u, y = q[i].v;
    			int minn = q[i].w, tmp = i;
    			if(dep[x] < dep[y]) swap(x,y);
    			while(dep[x] > dep[y])
    			{
    				if(minn > q[id[x]].w) tmp = id[x], minn = q[id[x]].w;
    				x = f[x];
    			}
    			while(x != y)
    			{
    				if(minn > q[id[x]].w) tmp = id[x], minn = q[id[x]].w;
    				if(minn > q[id[y]].w) tmp = id[y], minn = q[id[y]].w;
    				x = f[x]; y = f[y];
    			}
    			q[tmp].tag = 1; x = q[i].u; y = q[i].v;
    			if(tmp != i) q[i].w += minn;
    			if(dep[x] < dep[y]) swap(x,y);
    			while(dep[x] > dep[y])
    			{
    				if(id[x] != tmp) q[id[x]].w += minn;
    				x = f[x];
    			}
    			while(x != y)
    			{
    				if(id[x] != tmp) q[id[x]].w += minn;
    				if(id[y] != tmp) q[id[y]].w += minn; 
    				x = f[x]; y = f[y];
    			}
    		}
    	}
    	slove();
    	return 0;
    }
    

    T3 幽曲-埋骨于弘川 (buried)

    题意描述

    给定一个 (n) 个节点的树,其中 (1) 为根节点,每个点有点权,我们定义“子树”为若干条到根的链的并。给定一个无穷数列 ({a_n}) ,其构造方法如下:

    (a_n =egin{cases}1&x = 1\a[n-1] + maxDight(a[n-1]&x>1end{cases})

    其中 (maxDigit(n)) 函数为 (n) 的最大数位。

    这个无穷数列的前 (6) 项为 (1,2,4,8,16,22⋯) ,求原树有多少个子树先序遍历得到的权值序列包含在 ({a_n}) 中。

    数据范围:(sum nleq 500)

    solution

    神仙 (dp)(dp) 题,咕咕咕。

  • 相关阅读:
    下载及爬取网页内容
    对于for循环的理解
    记录安装fiddle出现的问题
    Django
    12种可以参考的思路关于代码能干什么
    “字符文本中字符太多”错误及解决方法
    jQuery参考:jquery中的$(document).ready()与window.onload的区别
    页面定时刷新功能实现
    HTML:关于位置的几个概念
    Lambda表达式
  • 原文地址:https://www.cnblogs.com/genshy/p/14541241.html
Copyright © 2011-2022 走看看