Beauty Of Unimodal Sequence
题意:给一个长度为n的数组,让你从中选出任意一组长度最长的单峰序列,要求输出这个单峰序列下标的最大字典序和最小字典序。
思路:贪心,对于字典序最小的下标,先正反跑LIS,求出第一个峰值,可知这个峰值对应的数字必选,因为已满足题意,且靠前。然后继续贪心选择这个位置前面满足条件的值字典序最小。
对于字典序最大的下标,则需要找到最后一个峰值。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=3e5+10; int box[maxn]; int ans1[maxn],ans2[maxn]; int len1[maxn],len2[maxn]; // 最长上升,下降序列 int num[maxn]; int a[maxn]; int main() { int n; while(~scanf("%d",&n)) { for(int i=1; i<=n; i++) scanf("%d",&a[i]); box[1]=-1; for(int i=1; i<=n; i++) box[i]=0; for(int i=1; i<=n; i++) ans1[i]=ans2[i]=0; int cnt=0; for(int i=1; i<=n; i++) { if(a[i]>box[cnt]) { box[++cnt]=a[i]; len1[i]=cnt; } else { len1[i]=lower_bound(box+1,box+1+cnt,a[i])-box; box[len1[i]]=a[i]; } } for(int i=1; i<=n; i++) box[i]=0; cnt=0; for(int i=n; i>=1; i--) { if(a[i]>box[cnt]) { box[++cnt]=a[i]; len2[i]=cnt; } else { len2[i]=lower_bound(box+1,box+1+cnt,a[i])-box; box[len2[i]]=a[i]; } } int maxlen=0,maxid; for(int i=1; i<=n; i++) { if((len1[i]+len2[i]-1)>maxlen) { maxlen=len1[i]+len2[i]-1; maxid=i; } } ans1[len1[maxid]]=maxid; // printf("%d? ",maxid); // printf("%d %d? ",a[maxid-1],a[ans1[len1[maxid-1]+1]]); for(int i=maxid-1; i>=1; i--) { if(a[i]<a[ans1[len1[i]+1]]) ans1[len1[i]]=i; } for(int i=maxid+1; i<=n; i++) { if(ans1[maxlen-len2[i]+1]==0&&a[i]<a[ans1[maxlen-len2[i]]]) ans1[maxlen-len2[i]+1]=i; } for(int i=1; i<=maxlen; i++) { if(i!=maxlen) printf("%d ",ans1[i]); else printf("%d",ans1[i]); } printf(" "); maxlen=0; for(int i=n; i>=1; i--) { if((len1[i]+len2[i]-1)>maxlen) { maxlen=len1[i]+len2[i]-1; maxid=i; } } ans2[len1[maxid]]=maxid; for(int i=maxid-1; i>=1; i--) { if(a[i]<a[ans2[len1[i]+1]]&&ans2[len1[i]]==0) ans2[len1[i]]=i; } for(int i=maxid+1; i<=n; i++) { if(a[i]<a[ans2[maxlen-len2[i]]]) ans2[maxlen-len2[i]+1]=i; } for(int i=1; i<=maxlen; i++) { if(i!=maxlen) printf("%d ",ans2[i]); else printf("%d",ans2[i]); } printf(" "); } }
Longest Subarray
题意:给n,c,k,给n个数字,已知数字都小于c,求一个合法连续数字串的最长长度,合法串要求出现的数字要么出现0次要么就必须出现次数>=k。
思路:从左到右枚举区间右端点,与此同时用线段树维护满足条件的左端点,线段树每个叶节点代表这个节点作为左端点时,所拥有的满足条件的数字个数(如果为C证明这个左端点可取)。从左到右枚举右端点时,判断每个新增的数字,
这个数字对前面的每个左端点拥有的合法数字个数有两点影响:
1.设目前右端点枚举到第i位,这个数字上一次出现的位置为pos,那么在[pos+1,i-1]这个区间内的点作为左端点时,i-1作为右端点时,是没有a[i]这个数字,而i作为右端点出现了a[i],所以[pos+1,i-1]合法个数需要-1.
2.设目前右端点枚举到第i位,这个数字这个数字目前出现记为第一次,往前的第k+1位的位置记为pos,第k位位置记为pos2,需要注意,在[pos+1,pos2]这个区间作为左端点,i-1作为右端点时,a[i]出现了K-1次,而枚举到右端点为i,新增了a[i]后[pos+1,i]这个区间中a[i]出现次数达到k次了,区间内的合法个数需要+1.
对于每次枚举一个右端点,线段树中叶子节点为C的位置就是合法的,[1,i]线性搜索太慢,所以需要线段树记录叶子节点的最大值和最大值的位置,在query时优先查询靠左边的同时满足最大值为C的位置。用vector记录每个数字出现的位置。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; vector<int> v[maxn]; int n,k,c; struct note { int left,right,maxx,lazy,ans; void up(int val) { maxx+=val; lazy+=val; } } tree[maxn*4]; void pushup(int id) { tree[id].maxx=max(tree[id<<1].maxx,tree[id<<1|1].maxx); if(tree[id].maxx==tree[id<<1].maxx) tree[id].ans=tree[id<<1].ans; else tree[id].ans=tree[id<<1|1].ans; } void pushdown(int id) { if(tree[id].lazy) { tree[id<<1].up(tree[id].lazy); tree[id<<1|1].up(tree[id].lazy); tree[id].lazy=0; } } void build(int id,int l,int r) { tree[id].left=tree[id].ans=l; tree[id].right=r; tree[id].maxx=tree[id].lazy=0; if(l==r) return; int mid=(l+r)/2; build(id<<1,l,mid); build(id<<1|1,mid+1,r); pushup(id); } int query(int id,int l,int r) { if(tree[id].maxx!=c) return -1; if(l<=tree[id].left&&tree[id].right<=r) return tree[id].ans; pushdown(id); int mid=(tree[id].left+tree[id].right)/2; if(r<=mid) return query(id<<1,l,r); else if(l>mid) return query(id<<1|1,l,r); else { int tmp; tmp=query(id<<1,l,mid); if(tmp!=-1) return tmp; else return query(id<<1|1,mid+1,r); } } void update(int id,int l,int r,int val) { if(l<=tree[id].left&&tree[id].right<=r) { tree[id].up(val); return; } pushdown(id); int mid=(tree[id].left+tree[id].right)/2; if(l<=mid) update(id<<1,l,r,val); if(r>mid) update(id<<1|1,l,r,val); pushup(id); } int main() { while(~scanf("%d%d%d",&n,&c,&k)) { for(int i=1; i<=c; i++) { v[i].clear(); v[i].push_back(0); } build(1,1,n); int ans=0; for(int i=1; i<=n; i++) { int x; scanf("%d",&x); update(1,i,i,c-1); if(v[x].back()<i-1) update(1,v[x].back()+1,i-1,-1); v[x].push_back(i); if(v[x].size()>=k+1) { int pos=v[x].size()-k-1; update(1,v[x][pos]+1,v[x][pos+1],1); } int tmp=query(1,1,i); if(tmp!=-1) ans=max(ans,i-tmp+1); } printf("%d ",ans); } }
I Love Palindrome String 回文自动机,哈希
#include<bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long const int maxn = 300005 ; const int N = 26 ; int nxt[maxn][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成 int fail[maxn] ;//fail指针,失配后跳转到fail指针指向的节点 int cnt[maxn] ; int num[maxn] ; int len[maxn] ;//len[i]表示节点i表示的回文串的长度 int S[maxn] ;//存放添加的字符 int last ;//指向上一个字符所在的节点,方便下一次add int n ;//字符数组指针 int p ;//节点指针 char a[maxn]; int id[maxn]; const ull hash1=201320111; ull ha[maxn],pp[maxn]; int ans[maxn]; ull getha(int l,int r) { if(l==1) return ha[r]; else return ha[r]-ha[l-1]*pp[r-l+1]; } int check(int l,int r) { int mid=(l+r)/2; if((r-l+1)%2==0) return getha(l,mid)==getha(mid+1,r); return getha(l,mid)==getha(mid,r); } int newnode ( int l ) //新建节点 { for ( int i = 0 ; i < N ; ++ i ) nxt[p][i] = 0 ; cnt[p] = 0 ; num[p] = 0 ; len[p] = l ; return p ++ ; } void init () //初始化 { p = 0 ; newnode ( 0 ) ; newnode ( -1 ) ; last = 0 ; n = 0 ; S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判 fail[0] = 1 ; } int get_fail ( int x ) //和KMP一样,失配后找一个尽量最长的 { while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ; return x ; } void add (int c ) { c -= 'a' ; S[++ n] = c ; int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置 if ( !nxt[cur][c] ) //如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串 { int now = newnode ( len[cur] + 2 ) ;//新建节点 fail[now] = nxt[get_fail (fail[cur])][c] ;//和AC自动机一样建立fail指针,以便失配后跳转 nxt[cur][c] = now ; num[now] = num[fail[now]] + 1 ; } last = nxt[cur][c] ; cnt[last] ++ ; id[last]=n; } void count () { for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ; //父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串! for(int i=2; i<p; i++) { if(check(id[i]-len[i]+1,id[i])) ans[len[i]]+=cnt[i]; // for(int j=id[i]-len[i]+1;j<=id[i];j++) // printf("%c",a[j]); // printf(" %d ",cnt[i]); } } int main() { pp[0]=1; for(int i=1; i<maxn; i++) pp[i]=pp[i-1]*hash1; while(~scanf("%s",a+1)) { int len1=strlen(a+1); for(int i=1; i<=len1; i++) ans[i]=0; ha[0]=0; for(int i=1; i<=len1; i++) ha[i]=ha[i-1]*hash1+a[i]; // printf("%d",check(1,7)); init(); for(int i=1; i<=len1; i++) add(a[i]); count(); for(int i=1; i<=len1; i++) { if(i!=n) printf("%d ",ans[i]); else printf("%d",ans[i]); } printf(" "); } }
Keen On Everything But Triangle
#include <cstdio> #include <vector> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll ; const int oo=0x7f7f7f7f ; const int maxn=4e5+7; const int mod=1e9+7; int n,m,cnt,root[maxn],a[maxn],x,y,k; struct node { int l,r,sum; } T[maxn*25]; vector<int> v; int getid(int x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1; } void update(int l,int r,int &x,int y,int pos) { T[++cnt]=T[y],T[cnt].sum++,x=cnt; if(l==r) return; int mid=(l+r)/2; if(mid>=pos) update(l,mid,T[x].l,T[y].l,pos); else update(mid+1,r,T[x].r,T[y].r,pos); } int query(int l,int r,int x,int y,int k) { if(l==r) return l; int mid=(l+r)/2; int sum=T[T[y].l].sum-T[T[x].l].sum; if(sum>=k) return query(l,mid,T[x].l,T[y].l,k); else return query(mid+1,r,T[x].r,T[y].r,k-sum); } int main(void) { while(~scanf("%d%d",&n,&m)) { v.clear(); for(int i=1; i<=n; i++) scanf("%d",&a[i]),v.push_back(a[i]); sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); for(int i=1; i<=n; i++) update(1,n,root[i],root[i-1],getid(a[i])); for(int i=1; i<=m; i++) { scanf("%d%d",&x,&y); ll ans=-1; int len=y-x+1; if(len>=3) { ll x1,x2,x3; x1=(ll)v[query(1,n,root[x-1],root[y],len)-1]; x2=(ll)v[query(1,n,root[x-1],root[y],len-1)-1]; x3=(ll)v[query(1,n,root[x-1],root[y],len-2)-1]; if(x1<x2+x3) ans=(ll)x1+x2+x3; else { for(int j=len-3; j>=1; j--) { x1=x2; x2=x3; x3=(ll)v[query(1,n,root[x-1],root[y],j)-1]; if(x1<x2+x3) { ans=(ll)x1+x2+x3; break; } } } } if(ans==-1) printf("-1 "); else printf("%lld ",ans); } for(int i=1; i<=n; i++) root[i]=0; for(int i=1; i<=25*n; i++) T[i].l=0,T[i].r=0,T[i].sum=0; } return 0; }