Codeforces Round #826 (Div. 3) E,F
E - Sending a Sequence Over the Network dp
写了半天的搜索,原来是dp,,,
dp[i]表示前i个位置是否合法,对于i来说,如果前i-1个是合法的,那么前i+a[i]也是合法的;如果前i-1-a[i]是合法的,那么前i个也是合法的,这样一直递推下去就行
Codeforces Round #826 (Div. 3) A - F - 知乎 (zhihu.com)
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define double long double
using namespace std;
const int mod=1e9+7;
const int inf=3e18;
double qpow(double a,int b)
{
double res=1;
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int dp[200005],t,n,a[200005];
signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
//freopen("in.txt", "r", stdin);
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],dp[i]=0;
dp[0]=1;
for(int i=1;i<=n;i++)
{
if(a[i]+i<=n) dp[i+a[i]]|=dp[i-1];
if(a[i]+1<=i) dp[i]|=dp[i-1-a[i]];
}
if(dp[n]) cout<<"YES\n";
else cout<<"NO\n";
}
system("pause");
return 0;
}
F - Multi-Colored Segments 线段树二分
如果第i个线段的[l,r]内还有颜色的话答案就是0,否则就要向左去找离l最近的点,向右去找离r最近的点,然后两个值再比较;
这样大概思路其实就有了,我们可以写一个区间加法区间求和的线段树,然后对出现的每个区间都加1,这样枚举到第i个线段[l,r]时,第i个线段的颜色为c,那么将所有颜色为c的线段区间都-1,也就是都去掉,然后如果[l,r]的和还是1的话说明有相交的线段,答案就是0,否则去左边[1,l]找离l最近的点,我们可以根据区间的和来判断这个区间的走向,因为是离l最近,那么优先找的区间一定是[(1+l),l],也就是右半段,放在函数里就是[mid+1,r],如果区间和大于等于1的话说明这个区间里一定是有符合条件的点的,那就再去这个区间搜,否则就要去[l,mid]区间里搜,对于去找右边离r最近的点的方法也是一样的;注意一个关键的小点,枚举答案的时候因为颜色是小于等于n的,所以可以直接去枚举颜色而不是去枚举线段,这样会减少很多复杂度;虽然这种方式好想,但是因为用了query函数所以多了个log,总的复杂度是o(nlognlogn);
l,r的范围比较大,离散化一下就可以,注意求答案做差的时候需要是原来的值做差,而不是离散后的
o(nlognlogn)的代码
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define double long double
using namespace std;
const int mod=1e9+7;
const int inf=3e18;
double qpow(double a,int b)
{
double res=1;
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
struct segtree
{
int val,lazy;
}tr[400005<<3];
void pushup(int p){tr[p].val=tr[p<<1].val+tr[p<<1|1].val;}
void build(int l,int r,int p)
{
tr[p].lazy=0;
if(l==r)
{
tr[p].val=0;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
pushup(p);
}
void pushdown(int p,int ln,int rn)
{
if(tr[p].lazy)
{
tr[p<<1].lazy+=tr[p].lazy;
tr[p<<1|1].lazy+=tr[p].lazy;
tr[p<<1].val+=tr[p].lazy*ln;
tr[p<<1|1].val+=tr[p].lazy*rn;
tr[p].lazy=0;
}
}
void update(int L,int R,int C,int l,int r,int p)
{
if(L<=l&&r<=R)
{
tr[p].val+=C*(r-l+1);
tr[p].lazy+=C;
return;
}
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
if(L<=mid) update(L,R,C,l,mid,p<<1);
if(R>mid) update(L,R,C,mid+1,r,p<<1|1);
pushup(p);
}
int query(int L,int R,int l,int r,int p)
{
if(L<=l&&r<=R) return tr[p].val;
if(L>r||R<l) return 0;
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
return query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
struct node
{
int l,r,c,id;
}a[400005];
vector<node>v[400005];
int t,n,b[400005],m,ans[400005];
void oper(int col,int x)
{
for(int i=0;i<v[col].size();i++)
{
int l=v[col][i].l,r=v[col][i].r;
update(l,r,x,1,m,1);
}
}
int check1(int l,int r)
{
if(query(l,r,1,m,1)<=0) return inf;
if(l==r) return l;
int mid=l+r>>1;
if(query(mid+1,r,1,m,1)>0) return check1(mid+1,r);
else return check1(l,mid);
}
int check2(int l,int r)
{
if(query(l,r,1,m,1)<=0) return inf;
if(l==r) return l;
int mid=l+r>>1;
if(query(l,mid,1,m,1)>0) return check2(l,mid);
else return check2(mid+1,r);
}
signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
//freopen("in.txt", "r", stdin);
cin>>t;
while (t--)
{
cin>>n;
m=0;
for(int i=1;i<=n;i++)
{
cin>>a[i].l>>a[i].r>>a[i].c;
b[++m]=a[i].l;b[++m]=a[i].r;
v[i].clear();
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;
build(1,m,1);
for(int i=1;i<=n;i++)
{
int l=lower_bound(b+1,b+m+1,a[i].l)-b;
int r=lower_bound(b+1,b+m+1,a[i].r)-b;
v[a[i].c].push_back(node{l,r,a[i].c,i});
update(l,r,1,1,m,1);
}
for(int col=1;col<=n;col++)
{
oper(col,-1);
for(int i=0;i<v[col].size();i++)
{
int l=v[col][i].l,r=v[col][i].r,id=v[col][i].id;
ans[id]=inf;
if(query(l,r,1,m,1)>0)
{
ans[id]=0;
continue;
}
int c1=check1(1,l),c2=check2(r,m);
if(c1!=inf) ans[id]=min(ans[id],b[l]-b[c1]);
if(c2!=inf) ans[id]=min(ans[id],b[c2]-b[r]);
}
oper(col,1);
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
还有一种方式是用multiset来二分的,将所有的l和r存入容器中,然后枚举的时候二分去找离l和r最近的点,感觉这个方法的复杂度是o(nlogn)的,但是好像没有想象中的那么优秀,就比上一个方法快100多ms;
Codeforces Round #826 (Div. 3) F // 线段树 - Jakon - 博客园 (cnblogs.com)
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define double long double
using namespace std;
const int mod=1e9+7;
const int inf=1e9+7;
double qpow(double a,int b)
{
double res=1;
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
struct segtree
{
int val,lazy;
}tr[400005<<3];
void pushup(int p){tr[p].val=tr[p<<1].val+tr[p<<1|1].val;}
void build(int l,int r,int p)
{
tr[p].lazy=0;
if(l==r)
{
tr[p].val=0;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
pushup(p);
}
void pushdown(int p,int ln,int rn)
{
if(tr[p].lazy)
{
tr[p<<1].lazy+=tr[p].lazy;
tr[p<<1|1].lazy+=tr[p].lazy;
tr[p<<1].val+=tr[p].lazy*ln;
tr[p<<1|1].val+=tr[p].lazy*rn;
tr[p].lazy=0;
}
}
void update(int L,int R,int C,int l,int r,int p)
{
if(L<=l&&r<=R)
{
tr[p].val+=C*(r-l+1);
tr[p].lazy+=C;
return;
}
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
if(L<=mid) update(L,R,C,l,mid,p<<1);
if(R>mid) update(L,R,C,mid+1,r,p<<1|1);
pushup(p);
}
int query(int L,int R,int l,int r,int p)
{
if(L<=l&&r<=R) return tr[p].val;
if(L>r||R<l) return 0;
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
return query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
struct node
{
int l,r,c,id;
}a[200005];
vector<node>v[200005];
multiset<int>L,R;
int t,n,b[400005],m,ans[400005];
void oper(int col,int x)
{
for(int i=0;i<v[col].size();i++)
{
int l=v[col][i].l,r=v[col][i].r;
update(l,r,x,1,m,1);
}
for(int i=0;i<v[col].size();i++)
{
int l=b[v[col][i].l],r=b[v[col][i].r];
if(x==-1)
{
L.erase(L.find(l));R.erase(R.find(r));
}
else
{
L.insert(l);R.insert(r);
}
}
}
signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
//freopen("in.txt", "r", stdin);
cin>>t;
while (t--)
{
cin>>n;
m=0;
L.clear();R.clear();
for(int i=1;i<=n;i++)
{
cin>>a[i].l>>a[i].r>>a[i].c; a[i].id=i;
b[++m]=a[i].l;b[++m]=a[i].r;
L.insert(a[i].l);R.insert(a[i].r);
v[i].clear();
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;
build(1,m,1);
for(int i=1;i<=n;i++)
{
int l=lower_bound(b+1,b+m+1,a[i].l)-b;
int r=lower_bound(b+1,b+m+1,a[i].r)-b;
v[a[i].c].push_back(node{l,r,a[i].c,i});
update(l,r,1,1,m,1);
}
for(int col=1;col<=n;col++)
{
if(v[col].size()==0) continue;
oper(col,-1);
for(int i=0;i<v[col].size();i++)
{
int l=v[col][i].l,r=v[col][i].r,id=v[col][i].id;
ans[id]=inf;
if(query(l,r,1,m,1)>0)
{
ans[id]=0;
continue;
}
auto it1=R.lower_bound(a[id].l);
if(it1!=R.begin()) ans[id]=min(ans[id],a[id].l-*prev(it1));
auto it2=L.lower_bound(a[id].r);
if(it2!=L.end()) ans[id]=min(ans[id],*it2-a[id].r);
}
oper(col,1);
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}
最后一种就是真正的o(nlogn)了,去掉了query函数,直接是写了一个二分函数,看懂了第一个方法,再看这个的写法也比较容易了,这其实也算是一个查询的函数了,不过这是查询下标而已
云剪贴板 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
上面的链接来自(26 封私信 / 85 条消息) 严格鸽 - 知乎 (zhihu.com)
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define double long double
using namespace std;
const int mod=1e9+7;
const int inf=3e18;
double qpow(double a,int b)
{
double res=1;
while(b)
{
if(b&1) res=res*a;
a=a*a;
b>>=1;
}
return res;
}
struct segtree
{
int val,lazy;
}tr[400005<<3];
void pushup(int p){tr[p].val=tr[p<<1].val+tr[p<<1|1].val;}
void build(int l,int r,int p)
{
tr[p].lazy=0;
if(l==r)
{
tr[p].val=0;
return;
}
int mid=l+r>>1;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
pushup(p);
}
void pushdown(int p,int ln,int rn)
{
if(tr[p].lazy)
{
tr[p<<1].lazy+=tr[p].lazy;
tr[p<<1|1].lazy+=tr[p].lazy;
tr[p<<1].val+=tr[p].lazy*ln;
tr[p<<1|1].val+=tr[p].lazy*rn;
tr[p].lazy=0;
}
}
void update(int L,int R,int C,int l,int r,int p)
{
if(L<=l&&r<=R)
{
tr[p].val+=C*(r-l+1);
tr[p].lazy+=C;
return;
}
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
if(L<=mid) update(L,R,C,l,mid,p<<1);
if(R>mid) update(L,R,C,mid+1,r,p<<1|1);
pushup(p);
}
int query(int L,int R,int l,int r,int p)
{
if(L<=l&&r<=R) return tr[p].val;
if(L>r||R<l) return 0;
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
return query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
struct node
{
int l,r,c,id;
}a[400005];
vector<node>v[400005];
int t,n,b[400005],m,ans[400005];
void oper(int col,int x)
{
for(int i=0;i<v[col].size();i++)
{
int l=v[col][i].l,r=v[col][i].r;
update(l,r,x,1,m,1);
}
}
int check1(int L,int l,int r,int p)
{
if(l>L) return inf;
if(r<=L)
{
if(tr[p].val<=0) return inf;
if(l==r) return l;
}
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
int pos=check1(L,mid+1,r,p<<1|1);
if(pos!=inf) return pos;
else return check1(L,l,mid,p<<1);
}
int check2(int R,int l,int r,int p)
{
if(r<R) return inf;
if(l>=R)
{
if(tr[p].val<=0) return inf;
if(l==r) return l;
}
int mid=l+r>>1;
pushdown(p,mid-l+1,r-mid);
int pos=check2(R,l,mid,p<<1);
if(pos!=inf) return pos;
else return check2(R,mid+1,r,p<<1|1);
}
signed main()
{
cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
//freopen("in.txt", "r", stdin);
cin>>t;
while (t--)
{
cin>>n;
m=0;
for(int i=1;i<=n;i++)
{
cin>>a[i].l>>a[i].r>>a[i].c;
b[++m]=a[i].l;b[++m]=a[i].r;
v[i].clear();
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;
build(1,m,1);
for(int i=1;i<=n;i++)
{
int l=lower_bound(b+1,b+m+1,a[i].l)-b;
int r=lower_bound(b+1,b+m+1,a[i].r)-b;
v[a[i].c].push_back(node{l,r,a[i].c,i});
update(l,r,1,1,m,1);
}
for(int col=1;col<=n;col++)
{
oper(col,-1);
for(int i=0;i<v[col].size();i++)
{
int l=v[col][i].l,r=v[col][i].r,id=v[col][i].id;
ans[id]=inf;
if(query(l,r,1,m,1)>0)
{
ans[id]=0;
continue;
}
int c1=check1(l,1,m,1);
int c2=check2(r,1,m,1);
if(c1!=inf) ans[id]=min(ans[id],b[l]-b[c1]);
if(c2!=inf) ans[id]=min(ans[id],b[c2]-b[r]);
}
oper(col,1);
}
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}