校内训练0611 围墙

【题目大意】

围墙可以看成是一个长度为 n 的括号序列,与此同时还有一个长度为 n 的排列 P,一个围墙被称为稳的,当且仅当:

(1)这个括号序列是合法的。

(2)构造一张 n 个点的图, 当且仅当第 i 个位置是左括号时, 点 i 向点 Pi 连边,最后形成的图必须满足每个点度数均为一。

保证对于任意 i 有 Pi!=i。

求一种合法方案。保证有解。

n <= 100

【题解】

我们考虑这是个置换,所以一定形成了很多不相交的环。

对于每个环,我们只能选一段、不选、选一段、不选这样交替下去。

显然只有偶环是有解的,所以只考虑偶环。

每个偶环有2种方案(第一个选,第一个不选),直接枚举是O(2^(n/2))的,复杂度接受不了。

我们发现,2元环的左括号一定放在前面更优(更容易形成括号序列),所以贪心放,剩下的最小是4元环,枚举即可,所以复杂度是O(2^(n/4))。

写个dfs然后发现常数太大。。。(还是过了)

# include <vector>
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 200 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, p[M], ans[M];
int h[M][M], hn[M], a[M], an;
int m; 

int head[M], nxt[M], to[M], tot = 0;
inline void add(int u, int v) {
    ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v;
}
inline void adde(int u, int v) {
    add(u, v), add(v, u);
}

bool vis[M];
inline void dfs(int x, int fa) {
    if(vis[x]) return ;
    vis[x] = 1;
    a[++an] = x;
    for (int i=head[x]; i; i=nxt[i])
        if(to[i] != fa) dfs(to[i], x);
}

inline void solve(int x) {
    an = 0;
    dfs(x, 0);
    if(an == 2) {
        if(a[1] < a[2]) ans[a[1]] = 1;
        else ans[a[2]] = 1;
    } else {
        ++m; hn[m] = an;
        for (int i=1; i<=an; ++i) h[m][i] = a[i];
    }
}

inline bool chk() {
    int sum = 0;
    for (int i=1; i<=n; ++i) {
        sum = sum + (ans[i] ? 1 : -1);
        if(sum < 0) return false;
    }
    for (int i=1; i<=n; ++i) putchar(ans[i] ? ‘(‘ : ‘)‘);
    puts("");
    return true;
}

bool ok;
inline void gans(int x) {
    if(ok) return ;
    if(x == m + 1) {
        if (chk()) ok = 1;
        return ;
    }
    for (int i=1; i<=hn[x]; ++i) ans[h[x][i]] = (i&1);
    gans(x+1);
    for (int i=1; i<=hn[x]; ++i) ans[h[x][i]] ^= 1;
    gans(x+1);
}

int main() {
    freopen("c.in", "r", stdin);
    freopen("c.out", "w", stdout);
    cin >> n;
    for (int i=1; i<=n; ++i) scanf("%d", &p[i]);
    for (int i=1; i<=n; ++i) adde(i, p[i]);
    for (int i=1; i<=n; ++i) if(!vis[i]) solve(i);
//    for (int i=1; i<=m; ++i, puts("\n"))
//        for (int j=1; j<=hn[i]; ++j)
//            printf("%d ", h[i][j]);
    gans(1);
    return 0;
}

时间: 2024-10-05 22:21:57

校内训练0611 围墙的相关文章

校内训练0611 矩阵

[题目大意] 给一个n*n的矩阵,每个位置为0或1.每次可以选择一行和一列,把那列完全赋值为那行的值.求最少多少步使得全为1. 无解输出-1. n <= 1000 [题解] 发现只有全空才是无解,否则考虑构造. 每一列,只要有0的格子都需要被赋值1次,所以设有x列有含有0的格子,则至少要赋值x次. 现在我们就需要一个全1的行来给这些有0的列赋值. 考虑构造一个全1的行.我们枚举第i行,假装要把他弄成全1的. 设第i行的0的数量为y,那么考虑第i列是否含有1,如果含有1那么就可以用含有1的那列的那

「csp校内训练 2019-10-24」解题报告

「csp校内训练 2019-10-24」解题报告 T1.猴猴吃苹果 \(Description\) 猴猴最喜欢在树上玩耍,一天猴猴又跳上了一棵树,这棵树有 \(N \ (N \leq 50000)\) 个苹果,每个苹果有一个编号,分别为 \(0\) ~ \(N - 1\) 它们之间由 \(N-1\) 个树枝相连,猴猴可以从树枝的一端爬到树枝的另一端,所以猴猴可以从任意一个苹果的位置出发爬到任意猴猴想去的苹果的位置. 猴猴开始在编号为 \(K \ (K < N)\) 的苹果的位置,并且把这个苹果吃

「csp校内训练 2019-10-30」解题报告

「csp校内训练 2019-10-30」解题报告 T1.树 题目链接(逃) \(Description\): 现在有一棵树,共 \(N\) 个节点. 规定:根节点为 \(1\) 号节点,且每个节点有一个点权. 现在,有 \(M\) 个操作需要在树上完成,每次操作为下列三种之一: \(1 \ x \ a\):操作 \(1\),将节点 \(x\) 点权增加 \(a\). \(2 \ x \ a\):操作 \(2\),将以节点 \(x\) 为根的子树中所有点的权值增加 \(a\). \(3 \ x\)

2017-4-7校内训练

丧病hzwer的ctsc训练赛 My AC:3/4 A.[Ctsc2014]企鹅QQ 思路:乱hash,我比较菜,写的丑代码各种WA+TLE,好久才A掉. #include<cstdio> #include<algorithm> using namespace std; #define ll long long #define MN 200 #define MX 6000000 #define MM 9000001 #define MOD1 890123798112473LL st

20170910校内训练

CCT 最近学校又发了n本五三题霸,BBS看到后十分高兴.但是,当他把五三拿到手后才发现,他已经刷过这些书了!他又认真地看了一会儿,发现新发的这些五三是2017版的,而他刷的是2016版的.现在他想找出所有他没有刷过的题来刷.每本五三都有m道题,并且它的特征(即它和去年版本的五三的差距)可以用一个m位二进制数来代表,二进制位上的1代表该题不同,0代表该题相同.比如4(100)就代表题目3和去年的有不同.5(101)就代表题目1和题目3和去年的有不同.而BBS热衷于给自己找麻烦,他要选择连续一段的

2017-3-3校内训练

hzwer出丧题虐人啦 ACM赛制 4/7 A.恼人的青蛙 题目大意:给定N*M矩阵上K个点,定义一条合法路径为从矩形外一点沿一条直线穿过矩形,每次走相同长度且在矩形内每步都要踩在给定点上,问经过给定点最多的路径经过几个点(若小于3输出0)(N,M,K<=5000). 思路:把点按横坐标第一关键字纵坐标第二关键字排序,f[i][j]表示有一条到i的路径,i上一个点是j,此时路径经过点数,每次确定i,j后就可以根据i,j算出j再前一个点的坐标,直接转移,复杂度O(K^2).评测机极慢稍微卡卡常才能

【三中校内训练】旅行

[题解] 显然的这是一道树形DP的题目 这里令f[i][0]为从i出发向以它为根的子树里走直到不能走的最大.最小价值 (不能走是什么自己阅读题目) 令s为x的儿子,w[i][j]为i和j之间的边的长度,则 f[x][0]=max(f[s][1]+w[s][i]) f[x][1]=min(f[s][0]+w[s][i]) 显然的通过这种方法,我们可以得到60分 可是,如何优化到线性呢 我们考虑一个节点x,x向某条边走的情况出现了很多次,浪费了很多时间 我们定义h[x][0/1]为从x出发,经过x的

校内训练0609 problem c

[题目大意] 给一棵树,求有多少条路径满足总和-最大值 是P的倍数 n<=10^5, P<=10^7 [题解] 一看就是点分治嘛 不考虑子树合并,考虑poj1741的做法,每次考虑经过重心的路径,用优先队列,从小到达添加并求答案即可. 容斥下. # include <queue> # include <stdio.h> # include <string.h> # include <iostream> # include <algorith

[3.10校内训练赛]

真的报警啦,hzwer又出一堆丧题虐人啦..... ------------------------------------------- A.[poj-1190]生日蛋糕 要做一个m层的蛋糕,每一层有高度和半径,且要分别比它上面的那一层的高度和半径大至少一,给定总体积n,求最小的侧面和顶上的面积之和m<=20,n<=10000 搜索....但是要加上比较强的剪枝. 1.如果此时的半径和高度无法建出剩余体积那么大的蛋糕,剪掉.这种情况我们不考虑半径和高度的减小,直接用((r-1)^2+(h-1