UVALive 6265 Graphic Madness 搜索 Regionals 2012 :: Europe - Central

其他题做法:

Non-boring sequences

求是否任意子串中,都有一个数,这个数只出现一次:

方法:跟矩形面积并一样,从右到左做,用线段树维护,对于i位置,如果线段树中恰好有n-i+1个点被覆盖了,说明

有解,否则无解。

如何构造线段?: 对于a(1)......a(i).......(N)括号里的数字表示位置,a表示一个数

有两个a,对于a(2)如果已经走过i位置,且目前在j位置j>1,那么可以知道[i,N]这个区间,[j ,x] ,x属于[i,N],a都是唯一的,因此对[i,N]加入一条线段

当到位置1时删除[i,N],加入[1,i-1]如此更新即可,

当然:还有通过类似快排的方法过的,但是也要通过记录每个数字前面,后面出现时的位置,

这样能确定一个数在这个区间是不是唯一的。

方法就是,对于一个区间,如果有一个唯一的数字,那么可以分成左右两个区间,分别判断即可。

注意会有超时的情况,数据是特殊构造的,既然是快排,如果只从一个方向开始,就会卡你

因此考虑一个分界点从两个方向同时进行判定,这样容易比较快找到分界点。

构造数据如下:


............DCDBCAB.

这种数据每次只能找到一个分界点,而且区间的规模只会是原来的N-2,会T掉(如果从左到右处理)

如果两个方向一起来,就很快找到分界点 了。跟快排的随机取点一样。贴一个队友的代码:线段树做的



#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = (int)2e5+100;
struct tree{
	int c, sum;
}t[maxn<<2];
int a[maxn], p[maxn], last[maxn], nxt[maxn];
int T, n, size;


inline void update(int x, int l, int r){
	if (t[x].c > 0){ t[x].sum = r - l + 1; }
	else if (l == r){ t[x].sum = 0; }
	else t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
}
void build(int x, int l, int r){
	t[x].c = t[x].sum = 0;
	if (l == r) return;
	int mid = l + r >> 1;
	build(x<<1, l, mid);
	build(x<<1|1, mid+1, r);
}

void insert(int x, int L, int R, int val, int l, int r){
	if (L <= l && r <= R){
		t[x].c += val;
		update(x, l, r);
		return;
	}
	int mid = l + r >> 1;
	if (L <= mid) insert(x<<1, L, R, val, l, mid);
	if (mid < R) insert(x<<1|1, L, R, val, mid+1, r);
	update(x, l, r);
}
char msg[2][15] = {"boring", "non-boring"};
int main()
{
	scanf("%d", &T);
	while(T--){
		bool flag = true;
		scanf("%d", &n);
		size = 0;
		for (int i = 0; i < n; i++){
			scanf("%d", &a[i]);
			p[size++] = a[i];
		}
		sort(p, p+size);
		size = unique(p, p+size) - p;
		for (int i = 0; i < n; i++){ a[i] = lower_bound(p, p+size, a[i]) - p; }
		for (int i = 0; i < size; i++) last[i] = n;
		for (int i = n-1; i >= 0; i--){
			nxt[i] = last[a[i]];
			last[a[i]] = i;
		}
//		puts("");
//		for (int i = 0; i < n; i++)
//			printf("%d\n", nxt[i]);
		build(1, 0, n-1);
		for (int i = n-1; i >= 0; i--){
			if (nxt[i] != n) insert(1, nxt[i], nxt[nxt[i]]-1, -1, 0, n-1);
			insert(1, i, nxt[i]-1, 1, 0, n-1);
//			printf("%d %d\n", i, t[1].sum);
			if (t[1].sum < n-1-i+1){ flag = false; break; }
		}
		printf("%s\n", msg[flag]);
	}
	return 0;
}





求一条路径走过两个树的所有点,并回到起点的方案。不存在输出NO。

两棵树的叶子结点是相互连通的,并且叶子结点相同,并且是一一对应的关系。


做法:先把两个树分成链,树要能分成不相交的链,起点终点都在叶子结点,那么就可以下一步了。

否则无解。判断的方法是任意非叶子点深搜一次,

记录need值,表示根为u的子树,有几个u的子树需要通过根才能连到其他叶子结点。

如果need>2说明无解,因为只能有一条路径经过u,

如果need=1说明u必须通过父亲才能连到其他结点。


分成链以后,对图进行一次深搜,一定会搜出一个环,判断环的大小是否恰好等于点数总和。

是就有解,输出即可。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
struct Edge{
    int u,v,next,use;
    Edge(){}
    void init(int _u,int _v,int _next,int _use){
        u =_u;
        v = _v;
        next = _next;
        use = _use;
    }
};
#define maxn 50000
Edge edge[maxn];
int cnt,head[maxn];
void addedge(int u,int v){
    edge[cnt].init(u,v,head[u],0);
    head[u] = cnt++;
    edge[cnt].init(v,u,head[v],0);
    head[v] = cnt++;
}

int ty[maxn];
char word[20];
int work(){
    int u=0;
    scanf("%s",word);
    if(word[0] == 'B') u+=4000;
    if(word[1] == 'S') u+=2000;
    int t = 0,len=strlen(word);
    for(int i = 2;i < len; i++)
        t = t*10+word[i]-'0';
    return u+t;
}
int ans;
int dfs(int u,int f){
    int need = 0,t;
    for(int i = head[u];i != -1; i= edge[i].next){
        int v = edge[i].v;
        if(v == f) continue;
        t = dfs(v,u);
        need += t;
        if(need > 2) ans = 0;
        if(t == 0) {
            edge[i].use = 1;
            edge[i^1].use = 1;
        }
    }
    if(need == 2) return 0;
    return 1;
}
int check[maxn];
vector<int>loop;
int dfs2(int u){
    if(check[u]) return 0;
    loop.push_back(u);
    check[u] = 1;
    for(int i = head[u]; i != -1;i=edge[i].next){
        int v = edge[i].v;
        if(edge[i].use == 0 && check[v] == 0){
            return dfs2(v)+1;
        }
    }
    return 1;
}


int main(){
    int t,n,k,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&k,&n,&m);
        memset(head,-1,sizeof(head));
        cnt = 0;
        int u,v;
        for(int i = 0;i < n+k-1; i++){
            u = work();
            v = work();
            addedge(u,v);
        }
        ans = 1;
        dfs(1,0);

        for(int i = 0;i < m+k-1; i++){
            u = work();
            v = work();
            addedge(u,v);
        }
        if(ans) dfs(4001,0);

        for(int i = 0;i < k; i++){
            u = work();
            v = work();
            addedge(u,v);
        }

        memset(check,0,sizeof(check));
        loop.clear();
        if(ans) {
            u = dfs2(1);
            if(u != n+k+k+m) ans = 0;
        }
        if(ans == 1){
            printf("YES");
            for(int i = 0;i < loop.size(); i++){
                printf(" ");
                if(loop[i] / 4000 == 0) printf("A");
                else printf("B");
                if(loop[i] % 4000 > 2000 ) printf("S");
                else printf("P");
                printf("%d",loop[i]%2000);

            }
            printf("\n");
        }
        else puts("NO");
    }
    return 0;
}

/*
1
2 1 11
AS1 AP1
AS2 AP1

BS1 BP1
BS2 BP11
BP1 BP2
BP2 BP3
BP3 BP4
BP4 BP5
BP5 BP6
BP6 BP7
BP7 BP8
BP8 BP9
BP9 BP10
BP10 BP11

AS1 BS2
BS1 AS2
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值