사취 서론 - ODT 코 도리 나무
72825 단어 사기 도 론
코 도리 트 리 (또는 ODT (Old Driver Tree) 라 고도 함) 라 는 독종 알고리즘 은 CodeForces - 896 C Willem, Chtholly and Seniorius 의 정 해 에서 파생 되 었 다.
랜 덤 데이터 에서 뛰 는 경우 가 많 지만 폭력 을 속 이 는 비 정통 알고리즘 사상 은 폭력 을 속 이 는 것 일 뿐 시간 복잡 도 는 정확 하지 않다 는 것 을 명심 해 야 한다.
언제 코 도리 나무 로
코 도리 나 무 는 일반적으로 선분 나무 가 해결 해 야 할 구간 류 문 제 를 해결 하 는 데 쓰 인 다.
그리고 코 도리 나 무 를 폭력 적 으로 초두 거리 로 만 드 는 관건 은 assign 구간 푸 시 작업 이다. 즉, 문제 에 구간 할당 작업 이 있어 야 하고 데이터 가 무 작위 적 인 상황 에서 복잡 도 를 보장 할 수 있다.
코 도리 나무 - 초기 화
일반적으로 코 도리 나 무 는 set 유지보수 수열 set 에서 각 노드 마다 세 개의 값 (l l, r, v a l) (ll, rr, val) (ll, rr, val) 을 선택 합 니 다. [l, r] [ll, rr] [ll, rr] 내의 값 은 모두 v a l val 입 니 다. 즉, 각 노드 마다 연 속 된 값 이 같은 구간 set 에서 각 구간 의 왼쪽 끝 점 으로 정렬 하 는 것 입 니 다.
struct node
{
int ll,rr;
mutable int val;
node(int L,int R=-1,int V=0): ll(L), rr(R), val(V) {}
bool operator < (const node& tt)const { return ll<tt.ll;}//
};
set<node> st;
주의해 야 할 것 은 m u t a b l e mutable mutable 이 v a l val 에 대한 수식 이 없 으 며 다음 add 함수 에서 CE 를 가 져 옵 니 다.
다음은 교체 기 에 대한 조작 을 편리 하 게 하기 위해 매크로 정 의 를 내 립 니 다.
#define IT set::iterator
코 도리 나무 - 분열 스 플 릿
s p l i t (p o s) split (pos) split (pos) 조작 은 원래 pos 위 치 를 포함 하 는 노드 를 두 부분 으로 나 누 어 [l, p o s - 1] [l, pos - 1] [l, pos - 1] 과 [p o s, r] [pos, r] [pos, r] 로 나 누 어 분 단 된 후 p o s pos 를 왼쪽 끝 점 으로 하 는 교체 기 를 말한다.
IT split(int pos)
{
IT it=st.lower_bound(node(pos));// pos
if(it!=st.end()&&it->ll==pos) return it;//pos ,
--it;// pos
int ll=it->ll,rr=it->rr,val=it->val;
st.erase(it);//
st.insert(node(ll,pos-1,val));
return st.insert(node(pos,rr,val)).first;// .first
}
코 도리 트 리 – 구간 할당 할당
코 도리 나 무 는 이 걸 로 부정 확 한 복잡 도 를 유지 하 는 거 야.
void assign(int ll,int rr,int val)
{
IT itr=split(rr+1),itl=split(ll);
st.erase(itl,itr);
st.insert(node(ll,rr,val));
}
필요 한 구간 을 분열 시 키 고 삭제 한 후 새로운 주 의 를 삽입 하여 [l, r] [ll, rr] [ll, rr] 구간 을 분열 시 킬 때 먼저 오른쪽 단점 을 분열 시 키 고 왼쪽 단점 을 분열 시 켜 야 한다.
e r a s e erase erase 방법 은 교체 기 가 설명 한 구간 [f i r s t, l a s t) [first, last) [first, last) 를 삭제 할 수 있 습 니 다. 왼쪽 닫 고 오른쪽 열 림 에 주의 하 십시오.
void erase (iterator first, iterator last)
데이터 가 순 무 작위 인 상황 에서 매번 a s i g n assign assign 의 구간 길 이 는 N / 3 N / 3 N / 3 으로 s e t set 규모 가 급 격 히 떨 어 진 후에 O (m l o g n) O (mlogn) O (mlogn) 에 가 까 운 현학 의 부정 확 한 복잡 도 에 이 르 렀 음 을 증명 할 수 있다.
코 도리 나무 - 기타 폭력 조작
다른 조작 은 정말 규칙 적 인 순 폭력 으로 대응 구간 을 직접 꺼 내 폭력 으로 모든 것 을 조작 하 는 것 이다.
보통 이렇게 생 겼 어 요.
void fun(int ll,int rr)
{
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
{
//...
}
}
밤 을 삶다
int qsum(int ll,int rr)
{
int res=0;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl) res+=(itl->rr-itl->ll+1)*itl->val;// (itl->rr-itl->ll+1)
return res;
}
좀 더 복잡 한 밤 을 끓 여 라 – 구간 Kth
똑 같은 폭력 이에 요.
#define pir pair // ,
int kth(int ll,int rr,int k)
{
vector<pir> vec;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
vec.push_back( pir(itl->val,itl->rr-itl->ll+1) );
sort(vec.begin(),vec.end());//
for(vector<pir>::iterator it=vec.begin();it!=vec.end();++it)
{
k-=it->second;
if(k<=0) return it->first;
}
return -1;
}
코 도리 나무 - 폭력 AC 응용
CodeForces - 896C Willem, Chtholly and Seniorious
독종 ODT 의 근원
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long lt;
#define IT set::iterator
#define pir pair
#define mkp(x,y) make_pair(x,y)
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
lt seed,vmax;
lt rnd()
{
lt res=seed;
seed=(seed*7+13)%1000000007;
return res;
}
const int maxn=100010;
int n,m;
lt a[maxn];
struct node
{
int ll,rr;
mutable lt val;
node(int L,int R=-1,lt V=0): ll(L), rr(R), val(V) {}
bool operator < (const node& tt)const { return ll<tt.ll;}
};
set<node> st;
lt qpow(lt a,lt k,lt p)
{
lt res=1; a%=p;
while(k>0){
if(k&1) res=(res*a)%p;
a=(a*a)%p; k>>=1;
}
return res;
}
IT split(int pos)
{
IT it=st.lower_bound(node(pos));
if(it!=st.end()&&it->ll==pos) return it;
--it;
int ll=it->ll,rr=it->rr;
lt val=it->val;
st.erase(it);
st.insert(node(ll,pos-1,val));
return st.insert(node(pos,rr,val)).first;
}
void assign(int ll,int rr,lt val)
{
IT itr=split(rr+1),itl=split(ll);
st.erase(itl,itr);
st.insert(node(ll,rr,val));
}
void add(int ll,int rr,lt val)
{
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl) itl->val+=val;
}
lt kth(int ll,int rr,int k)
{
vector<pir> vec;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
vec.push_back(pir(itl->val,itl->rr-itl->ll+1));
sort(vec.begin(),vec.end());
for(vector<pir>::iterator it=vec.begin();it!=vec.end();++it)
{
k-=it->second;
if(k<=0) return it->first;
}
return -1;
}
lt qsum(int ll,int rr,lt x,lt y)
{
lt res=0;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
res+=(qpow(itl->val,x,y)*((itl->rr-itl->ll+1)%y))%y,res%=y;
return res;
}
int main()
{
n=read();m=read();
seed=read();vmax=read();
for(int i=1;i<=n;++i)
{
a[i]=(rnd()%vmax)+1;
st.insert(node(i,i,a[i]));
}
for(int i=1;i<=m;++i)
{
int op=(rnd()%4)+1;
int ll=(rnd()%n)+1,rr=(rnd()%n)+1;
lt x,y;
if(ll>rr) swap(ll,rr);
if(op==3) x=(rnd()%(rr-ll+1))+1;
else x=(rnd()%vmax)+1;
if(op==4) y=(rnd()%vmax)+1;
if(op==1) add(ll,rr,x);
else if(op==2) assign(ll,rr,x);
else if(op==3) printf("%lld
",kth(ll,rr,x));
else if(op==4) printf("%lld
",qsum(ll,rr,x,y));
}
return 0;
}
낙 곡 P2572 [SCOI 2010] 시퀀스 조작
설명 할 게 없어 요. 폭력 이 니까 요.
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define IT set::iterator
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=100010;
int n,m;
int a[maxn];
struct node
{
int ll,rr;
mutable int val;
node(int L,int R=-1,int V=0): ll(L), rr(R), val(V) {}
bool operator < (const node& tt)const { return ll<tt.ll;}
};
set<node> st;
IT split(int pos)
{
IT it=st.lower_bound(node(pos));
if(it!=st.end()&&it->ll==pos) return it;
--it;
int ll=it->ll,rr=it->rr,val=it->val;
st.erase(it);
st.insert(node(ll,pos-1,val));
return st.insert(node(pos,rr,val)).first;
}
void assign(int ll,int rr,int val)
{
IT itr=split(rr+1),itl=split(ll);
st.erase(itl,itr);
st.insert(node(ll,rr,val));
}
void rev(int ll,int rr)
{
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl) itl->val^=1;
}
int qsum(int ll,int rr)
{
int res=0;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl) res+=(itl->rr-itl->ll+1)*itl->val;
return res;
}
int qmax(int ll,int rr)
{
int res=0,tt=0;
IT itr=split(rr+1),itl=split(ll);
for(;itl!=itr;++itl)
{
if(itl->val==0) res=max(res,tt),tt=0;
else tt+=itl->rr-itl->ll+1;
}
return max(res,tt);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
st.insert(node(i,i,a[i]));
}
for(int i=1;i<=m;++i)
{
int k=read(),ll=read()+1,rr=read()+1;
if(k==0) assign(ll,rr,0);
else if(k==1) assign(ll,rr,1);
else if(k==2) rev(ll,rr);
else if(k==3) printf("%lld
",qsum(ll,rr));
else if(k==4) printf("%lld
",qmax(ll,rr));
}
return 0;
}
낙 곡 P2787 국어 1 (chin1) - 이치 사고 문제 풀이
시공 소모량 초두 정 해 는 한 개 수량 급 이 아니다.
다시 한 번 일 깨 워 주다
코 도리 나무 가 실제 적 으로 아무리 우수 하 더 라 도 그것 은 데이터 랜 덤 의 결과 일 뿐이다. 결국은 폭력 적 인 사기 수단 일 뿐이다. ODT AC 를 여러 문제 사용 할 수 있 지만 시간 복잡 도가 정확 하지 않다 는 사실 을 바 꿀 수 없다.
평소에 연습 할 때 적 게 사용 하 는 것 을 권장 합 니 다. 왜냐하면 이것 은 문제 자체 가 풀 고 있 는 정수 시험장 에서 문제 에 대해 생각 이 없 는 것 이 좋 은 사기 수단 이기 때 문 입 니 다.