codeforces1343F

这道F题说句实在的,题意能把人看蒙了,看懂了倒是挺简单的,反正我一开始把题意看难了。

题目

题目大意

存在一个长度为 n n n的排列 a a a,然后给出 n − 1 n-1 n1个子排列,其中每个子排列的元素为 [ a l , a l + 1 , . . . , a r ] [a_l,a_{l+1},...,a_r] [al,al+1,...,ar],其中 r r r是由 2 − n 2-n 2n里面的数字,每一个数字必将出现一次。求原来的排列。

思想

看到这个每一个数字必将出现一次,就可能想到一个问题了,如果我把 r r r前面所有的数字全部确定了,那么第 r r r个数字也应该确定了!
证明:假设当前确定了 1 1 1 r − 1 r-1 r1的数字,那么可以发现如果把确定的数字在子排列中标记的话,那么将会出现一段子排列刚好只剩下一个数字没有标记,这就是第r个数字,同时还可以发现,这样的子排列只有一个,因为如果有两个的话,那么第r个数字就有两个,可是只有一个位置,很明显冲突了,可用作剪枝。注意这里只是一个必要条件,它不能充分保证得出的排列一定成立,但是能保证原排列一定符合这个性质,所以我们还需要判断是否符合条件。其时间复杂度为 n 3 ∗ l o g 2 n n^3*log_2 n n3log2n

实现

想挺容易的,做起来我真的没想到, s e t set set这么骚,要不是学了一点面向对象,我差点理解不了。
每一个子序列都可以放到一个集合里面,每一次标记都是把这个数字从集合里面删除。判断的时候直接看集合是否相同。
介绍几个骚操作:以 s e t < i n t > d set<int> d set<int>d为例

  1. *d.begin()为遍历set
  2. set<set<int> > d(q.begin(),q.end())q在这里为vector<set<int> > q,这个语句的作用是将q这个数组的所有元素存到d这个集合里面。
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <vector>
#include<set>
using namespace std;
const int N = 2e5+5;
typedef long long ll;
#define rep(i,a,b) for(i=(a);i<=b;i++)
#define pt(a) printf("%d\n",(a))

int main()
{
    int n,m,i,j,k,t=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        vector<set<int> > q;
        vector<int> ans;
        rep(i,1,n-1){
            scanf("%d",&m);
            set<int> cur;
            rep(j,1,m){
                int x;
                scanf("%d",&x);
                cur.insert(x);
            }
            q.push_back(cur);
        }
        rep(i,1,n){
            ans.clear();
            vector<set<int> > p = q;
            int cur = i;
            ans.push_back(cur);
            rep(j,1,n-1){
                int pos = 0 , cnt = 0;
                rep(k,0,n-2){
                    if(p[k].count(cur)) p[k].erase(cur);
                    if(p[k].size()==1) cnt++,pos = *p[k].begin();//遍历set
                }
                if(cnt!=1) break;
                else cur = pos,ans.push_back(cur);
            } 
            if(j==n){
                set<set<int> > d(q.begin(),q.end());//自动把q数组全部放到set里面
                bool ok = true;
                for(int r=1;r<n;r++){
                    set<int> f;
                    f.clear();
                    bool flag = false;
                    for(int l=r;l>=0;l--){
                        f.insert(ans[l]);
                        //printf("%d ",ans[l]);
                        if(d.count(f)) {
                            flag = true;
                            break;
                        }
                    }
                   // printf("yes%d %d\n",i,flag);
                    if(!flag) {
                        ok = false;
                        break;
                    }
                }
                if(ok){
                    for(int l=0;l<n;l++){
                        printf("%d ",ans[l]);
                    }
                    printf("\n");
                    break;
                }
            }
        }
    }
    //system("pause");
    return 0;
}

参考资料

cf题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值