其他题做法:
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 */