博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一些简单题(2)(Source : NOIP历年试题+杂题)
阅读量:4651 次
发布时间:2019-06-09

本文共 12333 字,大约阅读时间需要 41 分钟。

给出$m$个区间$[l_i,r_i]$覆盖$S=[1,n]$,试确定最大特殊点的数使得这每一个区间覆盖且仅覆盖一个特殊点。

如果无解,输出$-1$ 对于100%的数据$n\leq 2\times 10^5 , m\leq 10^5$,数据较弱。

Sol1 : 设$ w_i$ 表示$ [1,i] $特殊点数目,那么对于每个区间的约束,有且仅有$1$个就等价于$w_r-w_{l-1}=1$

我们考虑每一个点只可能是特殊点或不是特殊点,那么就是$0 \leq w_i - w_{i-1} \leq 1$

又上面的式子$w_r-w_{l-1}=1$也可以化成不等式来表示$1 \leq w_r-w_{l-1} \leq 1$

也就是$w_r - w_{l-1} \leq 1 , w_{l-1} -w_r \leq -1$ , 结合$w_{i-1} - w_i \leq 0$我们就可以跑差分约束系统了。

把$dist\{ i,i-1 \} =0 , dist\{l-1,r\}=1 ,dist\{r,l-1\}=-1$建图跑SPFA(注意判负环!!!)然后到时间输出-1弹出就可以啦。

复杂度$O($卡时间$)$

#include
#include
#include
#include
#include
#define Endl endl;//qwqusing namespace std;const int maxn=1e6;int n,m,cnt=1;int head[maxn],vis[maxn],dis[maxn];struct node{ int nxt; int to; int w;}edge[maxn];void add(int x,int y,int z){ edge[cnt].to=y;edge[cnt].w=z;edge[cnt].nxt=head[x];head[x]=cnt++;}//int cn[maxn];int l,r;int tot=0;int spfa(){ deque
q;//双端队列优化spfa memset(dis,0x3f,sizeof(dis)); dis[0]=0;//从0开始的原因见下面 vis[0]=1; q.push_back(0); while(q.size()) { int x=q.front(); q.pop_front(); vis[x]=0; for(int i=head[x];i;i=edge[i].nxt) { int y=edge[i].to,z=edge[i].w; if(dis[y]>dis[x]+z) { dis[y]=dis[x]+z; //cn[y]=cn[x]+1; if(!vis[y]) { if(++tot>1926817) return -1;//人要有点信仰,这是梦想spfa的核心部分 //if(cn[y]>n)return -1;这是正儿八经的判负环,请一定要学会 vis[y]=1; if(q.size()&&dis[y]>dis[q.front()])q.push_back(y); else q.push_front(y); } } } } return dis[n];}int main(){ cin>>n>>m; for(int i=1;i<=m;i++) { cin>>l>>r; add(l-1,r,1); add(r,l-1,-1); } for(int i=1;i<=n;i++) add(i,i-1,0),add(i-1,i,1);//i-1可能为0,所以上面最短路要从0开始跑(前后呼应~~手动滑稽) cout<
<
P3084.cpp-某std

Sol2:作为一个不会差分约束的OIer,一个很显然的想法是线性序列上的Dp。

若不考虑-1的情况,这个Dp应该是可以求出结果的。

设$ f_i $ 表示到第$i$个点为止,且第$i$点为特殊点的最大方案数。

转移可以为$f_i = \max_{i=l_i}^{r_i} f_j + 1$

主要是求取值范围$l_i,r_i$,对于一条线段$[x,y]$把$l_{y+1} = max(l_{y+1},x) , r_y = min(r_y,x - 1)$

然后由于$l[]$前面比后面范围大,所以逐步紧缩,$l_i = max\{l_{i-1},l_i\}$

然后由于$r[]$后面比前面范围大,所以逐步紧缩,$r_i = min\{r_{i+1},r_i\}$

然后做一个dp就可以了(可以单调队列\线段树优化转移!!!)

复杂度$O(n log_2 n)/O(n)/O(nm)[玄学随机数据]$

# include 
# define int long longusing namespace std;const int N=2e5+10;int l[N],r[N],f[N];int n,m;signed main(){ scanf("%lld%lld",&n,&m); for (int i=1;i<=n+1;i++) r[i]=i-1; for (int i=1;i<=m;i++) { int x,y; scanf("%lld%lld",&x,&y); l[y+1]=max(l[y+1],x); r[y]=min(r[y],x-1); } for (int i=2;i<=n+1;i++) l[i]=max(l[i-1],l[i]); for (int i=n;i>=1;i--) r[i]=min(r[i+1],r[i]); int ans=-1; for (int i=1;i<=n;i++) { bool st=false; for (int j=l[i];j<=r[i];j++) f[i]=max(f[i],f[j]+1),st=true; if (!st) f[i]=-1; ans=max(ans,f[i]); } printf("%lld\n",ans); return 0;}
P3084-90pts(无优化)
# include 
# define int long longusing namespace std;const int N=2e5+10;int n,m,l[N],r[N],f[N],q[N],ql,qr;void add(int x) { if (f[x]==-1) return; while (qr>=ql&&f[q[qr]]<=f[x]) qr--; q[++qr]=x;}signed main(){ scanf("%lld%lld",&n,&m); for (int i=0;i<=n+1;i++) r[i]=i-1,l[i]=-1; for (int i=1;i<=m;i++) { int x,y; scanf("%lld%lld",&x,&y); r[y]=min(r[y],x-1); l[y+1]=max(l[y+1],x-1); } for (int i=1;i<=n+1;i++) l[i]=max(l[i],l[i-1]); for (int i=n;i>=1;i--) r[i]=min(r[i],r[i+1]); f[0]=0; ql=1,qr=0; for (int i=1;i<=n+1;i++) { for (int j=r[i-1];j<=r[i];j++)add(j); while (qr>=ql&&q[ql]<=l[i]) ql++; if (qr
P3084-100pts(单调队列)

一个$n$个点$m$个边可重边,可不连通的无向图,$q$个询问,$u -> v$所有路径上最小值最大是多少。

对于100%的数据$n\leq 10^5 , m\leq 5\times 10^5 , q\leq 3\times 10^4 $

Sol:一个比较简单的结论,在每一个连通块中所有点对走的路径必然是按照最大生成树上路径走的。

证明如下,设在一个连通块内$dist(u,v) < dist(u,t) + dist(t,v)$那么必然的,如果需要保证$(t,u,v)$连通我可以在之中随意删掉一条边同时加上一条边。

也就是说,构造断开$(u,v)$的这条边加上一条$(u,t)$或者$(v,t)$,这样明显最大生成树要更大,和已知的最大生成树相矛盾。

然后转化为树上问题,可以搞LCA了。

复杂度$O((q+n) {log_2}^2 n)$

# include 
# define int long longusing namespace std;const int N=1e5+10;struct rec{ int pre,to,w;}a[N<<1];int d[N][25],g[N][25],f[N],head[N],dep[N];int tot,n,m;bool vis[N];struct Edge{ int u,v,w; };vector
E;bool cmp(Edge a,Edge b) { return a.w>b.w;}inline int read(){ int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X;}inline void write(int x){ if (x<0) putchar('-'),x=-x; if (x>9) write(x/10); putchar('0'+x%10);}void adde(int u,int v,int w){ a[++tot].pre=head[u]; a[tot].to=v; a[tot].w=w; head[u]=tot;}int father(int x) { if (f[x]==x) return x; return f[x]=father(f[x]);}void kruskal(){ sort(E.begin(),E.end(),cmp); for (int i=1;i<=n;i++) f[i]=i; for (int i=0;i
=0;i--) if (dep[g[u][i]]>=dep[v]) ret=min(ret,d[u][i]),u=g[u][i]; if (u==v) return ret; for (int i=21;i>=0;i--) if (g[u][i]!=g[v][i]) { ret=min(ret,d[u][i]); ret=min(ret,d[v][i]); u=g[u][i]; v=g[v][i]; } ret=min(ret,d[u][0]); ret=min(ret,d[v][0]); return ret; }signed main(){ n=read();m=read(); for (int i=1;i<=m;i++) { int u=read(),v=read(),w=read(); E.push_back((Edge){u,v,w}); } kruskal(); init(); int T=read(); while (T--) { int u=read(),v=read(); write(getans(u,v)); putchar('\n'); } return 0;}
P1967.cpp

有$n$个国家,给定一个含有$m$个节点的环,$k$次事件,每次可以对$[l,r]$之间的节点区间加$d$,

询问至少几次事件后,$n$个国家权值经过多少次事件后获得权值不小于$p_i$。

对于100%的数据 $ n,m,k\leq 3 \times 10^5$ , 数据随机。

Sol :离线后整体二分,考虑队列$ [l,r] $中的所有点的答案都在$ [L,R] $之间。

如果当前答案$mid = \left \lfloor \frac{L+R}{2} \right \rfloor$  是不是可以,

如果可以就表示事件发生次数可以越少,否则,事件发生次数必须越多。

这里可以使用一个技巧,类似于莫队的指针now表示当前发生到次数多少了,如果不够往上加,否则往下减。

这样答案可以保证和$r-l$同级的复杂度。

然后使用树状数组维护区间修改,单点查询。

最后注意一下加起来爆longlong问题:一旦超过了$p_i$的权便不再累加。

# include 
# define int long long# define inf (0x7f7f7f7f)using namespace std;const int N=3e5+10;struct rec{ int l,r,w;}a[N];vector
Vd[N];int n,m,T;int p[N],q[N],t[N],ans[N],cnt[N],q1[N],q2[N];inline int read(){ int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X;}int lowbit(int x){
return x&(-x);}void update(int x,int d){
for (;x<=m;x+=lowbit(x)) t[x]+=d;}int query(int x) { int ret=0; for (;x;x-=lowbit(x)) ret+=t[x]; return ret;}void modify(int l,int r,int d){update(l,d); update(r+1,-d);}void change(int l,int r,int w){ if (l<=r) { modify(l,r,w);} else { modify(l,m,w); modify(1,r,w);}}int now=0;void solve(int l,int r,int L,int R){ if (l>r||L>R) return; if (L==R) { for (int i=l;i<=r;i++) ans[q[i]]=L; return; } int mid=(L+R)>>1,t1=0,t2=0; while (now
mid) change(a[now].l,a[now].r,-a[now].w),--now; for (int i=l;i<=r;i++) { cnt[q[i]]=0; for (int j=0;j
p[q[i]]) break; } } for (int i=l;i<=r;i++) { if (cnt[q[i]]>=p[q[i]]) q1[++t1]=q[i]; else q2[++t2]=q[i]; } for (int i=1;i<=t1;i++) q[l+i-1]=q1[i]; for (int i=1;i<=t2;i++) q[l+t1+i-1]=q2[i]; solve(l,l+t1-1,L,mid); solve(l+t1,r,mid+1,R);}signed main(){ n=read();m=read(); for (int i=1;i<=m;i++) Vd[read()].push_back(i); for (int i=1;i<=n;i++) p[i]=read(),q[i]=i; T=read(); for (int i=1;i<=T;i++) a[i].l=read(),a[i].r=read(),a[i].w=read(); T++; a[T].l=1,a[T].r=m,a[T].w=inf; solve(1,n,1,T); for (int i=1;i<=n;i++) if (ans[i]==T) puts("NIE"); else printf("%lld\n",ans[i]); return 0;}
P3527.cpp

给出一个$W \times W$的正方形,支持下面2个操作:

1. $ [x,y,A] $向$ (x,y) $这个格子单点$ +A $

2.$[x1,y1,x2,y2]$ 查询子矩阵权值和,保证 $ (x_1,y_1) $在$ (x_2,y_2) $非严格左下方

对于100%的数据$1 \leq W\leq 2\times 10^6,1 \leq 操作数\leq 2\times 10^5$

Sol : 考虑数据范围那么大二维权值线段树或者树状数组会MLE,所以考虑离线的方法。

直接cdq,考虑一个转化,就是每一个格子 (操作的时间,x坐标,y坐标)可以看做一个三元组,直接三维偏序就可以了。

然后对于操作2用容斥原理拆成$ Ans = (x_2,y_2)-(x_2,y_1-1)-(x_1-1,y_2)+(x_1-1,y_1-1)$

注意到这样数组的下标会为0,而lowbit(0) =0 所以整体右移,对应的w的值也需要+1.

# include 
using namespace std;const int N=2e6+10;int tot,w;int t[N],ans[N],tim[N];bool mark[N];int lowbit(int x) { return x&(-x);}inline int read(){ int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X;}void update(int x,int d){x++; for (;x<=w+1;x+=lowbit(x)) t[x]+=d; }int query(int x) { int ret=0; x++; for (;x;x-=lowbit(x)) ret+=t[x]; return ret;}struct node{ int op,id,x,y,w;}a[N],b[N];struct aaa{ int x,d;}tmtm[N];int cnt;void cdq(int l,int r){ if (l==r) return; int mid=(l+r)>>1; cdq(l,mid); cdq(mid+1,r); int t1=l,t2=mid+1;cnt=0; for (int i=l;i<=r;i++) { if ((t1<=mid&&a[t1].x<=a[t2].x)||t2>r) { if (a[t1].op==1) { update(a[t1].y,a[t1].w); cnt++; tmtm[cnt].x=a[t1].y,tmtm[cnt].d=a[t1].w; } b[i]=a[t1++]; } else { if (a[t2].op==2) ans[a[t2].id]+=query(a[t2].y); b[i]=a[t2++]; } } for (int i=1;i<=cnt;i++) update(tmtm[i].x,-tmtm[i].d); for (int i=l;i<=r;i++) a[i]=b[i];}int main(){ memset(mark,false,sizeof(mark)); while (true) { int opt=read(); if (opt==0) w=read(); else if (opt==1) { int x=read(),y=read(),w=read(); a[++tot]=(node) {
1,tot,x,y,w}; } else if (opt==2) { int x1=read(),y1=read(),x2=read(),y2=read(); mark[tot+1]=true; a[++tot]=(node) {
2,tot,x2,y2,0}; a[++tot]=(node) {
2,tot,x2,y1-1,0}; a[++tot]=(node) {
2,tot,x1-1,y2,0}; a[++tot]=(node) {
2,tot,x1-1,y1-1,0}; } else if (opt==3) break; } cdq(1,tot); for (int i=1;i<=tot;i++) if (mark[i]) printf("%d\n",ans[i]-ans[i+1]-ans[i+2]+ans[i+3]); return 0;}
P4390.cpp

给出一个$n \times n$的矩阵,支持询问子矩阵的$K$小值。

对于100%的数据$n\leq 500,Q\leq 60000$

Sol : 直接整体二分,注意二维树状数组的写法(不要像1维树状数组那么写,中间那一维需要重新开始)。

         对于每一层二分,矩阵中的元素最多被插入到树状数组一次,也就是说最大复杂度是$O(n^2 {log_2}^2 n)$

         对于每一层,每个询问最多只可能被处理一次,即复杂度$O(m  {log_2}^2 n)$

         最后二分的总层数最大是$log_2 INF,INF=10^9$

         离散化后,层数就是严格的$log_2 n$层了,

         复杂度大概是$O((n^2 + Q){log_2}^3 n)$

// luogu-judger-enable-o2# include 
using namespace std;const int N=505;const int Q=60005;struct rec{ int x1,y1,x2,y2,x,y,v,id,op,k;}a[N*N+Q],q1[N*N+Q],q2[N*N+Q];int c[N][N],ans[N*N+Q];int n,m,tot;# define lowbit(x) (x&(-x))inline int read(){ int X=0,w=0; char c=0; while(c<'0'||c>'9') {w|=c=='-';c=getchar();} while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar(); return w?-X:X;}inline void write(int x){ if (x>9) write(x/10); putchar('0'+x%10);}void update(int x,int y,int d){ x++; y++; for (int i=x;i<=n+1;i+=lowbit(i)) for (int j=y;j<=n+1;j+=lowbit(j)) c[i][j]+=d;}int query(int x,int y){ x++; y++; int ret=0; for (int i=x;i;i-=lowbit(i)) for (int j=y;j;j-=lowbit(j)) ret+=c[i][j]; return ret; }int SubSum(int x1,int y1,int x2,int y2){ return query(x2,y2)-query(x2,y1-1)-query(x1-1,y2)+query(x1-1,y1-1);}void solve(int l,int r,int L,int R){ if (l>r||L>R) return; if (L==R) { for (int i=l;i<=r;i++) ans[a[i].id]=L; return; } int mid=(L+R)/2,t1=0,t2=0; for (int i=l;i<=r;i++) { if (a[i].op==1) { if (a[i].v<=mid) update(a[i].x,a[i].y,1),q1[++t1]=a[i]; else q2[++t2]=a[i]; } else if (a[i].op==2) { int t=SubSum(a[i].x1,a[i].y1,a[i].x2,a[i].y2); if (t>=a[i].k) q1[++t1]=a[i]; else a[i].k-=t,q2[++t2]=a[i]; } } for (int i=1;i<=t1;i++) if (q1[i].op==1) update(q1[i].x,q1[i].y,-1); for (int i=1;i<=t1;i++) a[l+i-1]=q1[i]; for (int i=1;i<=t2;i++) a[l+t1+i-1]=q2[i]; solve(l,l+t1-1,L,mid); solve(l+t1,r,mid+1,R);}int main(){ n=read();m=read(); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) { tot++;a[tot].op=1; a[tot].x=i,a[tot].y=j,a[tot].id=tot; a[tot].v=read(); } for (int i=1;i<=m;i++) { tot++; a[tot].id=tot; a[tot].op=2; a[tot].x1=read();a[tot].y1=read();a[tot].x2=read(); a[tot].y2=read();a[tot].k=read(); } solve(1,tot,0,1e9); for (int i=n*n+1;i<=tot;i++) write(ans[i]),putchar('\n'); return 0;}
P1527.cpp

 

转载于:https://www.cnblogs.com/ljc20020730/p/10886386.html

你可能感兴趣的文章
如何配置Smarty模板
查看>>
hdu1222
查看>>
从函数返回数组
查看>>
Rsync学习之旅上
查看>>
eclipse简单使用
查看>>
通过Ollydbg定位私有协议通信明文
查看>>
SWIFT4.0学习01 - 函数的命名、调用以及注意事项
查看>>
这个世界是那样的似曾相识
查看>>
python-生成器
查看>>
使用jenkins进行Android的持续集成
查看>>
2018-2019-1 20165234 《信息安全系统设计基础》第八周学习总结
查看>>
Linux指令
查看>>
练兵场
查看>>
pypi 打包分发
查看>>
【UOJ 50】树状数组2
查看>>
Centos 7 学习之静态IP设置
查看>>
(一)flask-sqlalchemy的安装和配置
查看>>
SharePoint Designer cannot open site error " the server could not complete your request"
查看>>
期末项目设计图-教务管理系统
查看>>
一、HTML语言
查看>>