「ZJOI2016」解题报告

我大浙的省选题真是超级神仙……这套已经算是比较可做的了。

「ZJOI2016」旅行者

神仙分治题。

对于一个矩形,每次我们从最长边切开,最短边不会超过 \(\sqrt{n\times m}\),所以对于每个点跑一遍最短路就行了。

时间复杂度 \(O(n\sqrt{n}\log n+q\sqrt{n})\)

\(Code\ Below:\)

#include <bits/stdc++.h>
#define id(i,j) (((i)-1)*m+(j))
using namespace std;
const int maxn=20000+10;
const int maxm=100000+10;
const int inf=0x3f3f3f3f;
int n,m,Q,val[maxn][4],dis[maxn],vis[maxn],ans[maxm];
int nx[4]={0,1,0,-1},ny[4]={1,0,-1,0}; struct node{
int x,y,dis;
node(int x=0,int y=0,int dis=0):x(x),y(y),dis(dis){}
};
inline bool operator < (const node &a,const node &b){
return a.dis>b.dis;
} struct Query{
int x1,x2,y1,y2,id;
}q[maxm],q1[maxm],q2[maxm]; inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
} inline void Dijkstra(int sx,int sy,int lx,int ly,int rx,int ry){
priority_queue<node> pq;
for(int i=lx;i<=rx;i++)
for(int j=ly;j<=ry;j++) dis[id(i,j)]=inf,vis[id(i,j)]=0;
dis[id(sx,sy)]=0;pq.push(node(sx,sy,0));
node u;int x,y;
while(!pq.empty()){
u=pq.top();pq.pop();
if(vis[id(u.x,u.y)]) continue;
vis[id(u.x,u.y)]=1;
for(int i=0;i<4;i++){
x=u.x+nx[i];y=u.y+ny[i];
if(x<lx||x>rx||y<ly||y>ry) continue;
if(dis[id(x,y)]>dis[id(u.x,u.y)]+val[id(u.x,u.y)][i]){
dis[id(x,y)]=dis[id(u.x,u.y)]+val[id(u.x,u.y)][i];
pq.push(node(x,y,dis[id(x,y)]));
}
}
}
} void solve(int lx,int ly,int rx,int ry,int ql,int qr){
if(ql>qr) return ;
if(lx==rx&&ly==ry){
for(int i=ql;i<=qr;i++) ans[q[i].id]=0;
return ;
}
if(rx-lx>ry-ly){
int mid=(lx+rx)>>1,cnt1=0,cnt2=0;
for(int i=ly;i<=ry;i++){
Dijkstra(mid,i,lx,ly,rx,ry);
for(int j=ql;j<=qr;j++) ans[q[j].id]=min(ans[q[j].id],dis[id(q[j].x1,q[j].y1)]+dis[id(q[j].x2,q[j].y2)]);
}
for(int i=ql;i<=qr;i++){
if(q[i].x1<=mid&&q[i].x2<=mid) q1[++cnt1]=q[i];
if(q[i].x1>mid&&q[i].x2>mid) q2[++cnt2]=q[i];
}
for(int i=1;i<=cnt1;i++) q[ql+i-1]=q1[i];
for(int i=1;i<=cnt2;i++) q[ql+cnt1+i-1]=q2[i];
solve(lx,ly,mid,ry,ql,ql+cnt1-1);
solve(mid+1,ly,rx,ry,ql+cnt1,ql+cnt1+cnt2-1);
}
else {
int mid=(ly+ry)>>1,cnt1=0,cnt2=0;
for(int i=lx;i<=rx;i++){
Dijkstra(i,mid,lx,ly,rx,ry);
for(int j=ql;j<=qr;j++) ans[q[j].id]=min(ans[q[j].id],dis[id(q[j].x1,q[j].y1)]+dis[id(q[j].x2,q[j].y2)]);
}
for(int i=ql;i<=qr;i++){
if(q[i].y1<=mid&&q[i].y2<=mid) q1[++cnt1]=q[i];
if(q[i].y1>mid&&q[i].y2>mid) q2[++cnt2]=q[i];
}
for(int i=1;i<=cnt1;i++) q[ql+i-1]=q1[i];
for(int i=1;i<=cnt2;i++) q[ql+cnt1+i-1]=q2[i];
solve(lx,ly,rx,mid,ql,ql+cnt1-1);
solve(lx,mid+1,rx,ry,ql+cnt1,ql+cnt1+cnt2-1);
}
} int main()
{
memset(val,inf,sizeof(val));
memset(ans,inf,sizeof(ans));
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<m;j++) val[id(i,j)][0]=val[id(i,j+1)][2]=read();
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++) val[id(i,j)][1]=val[id(i+1,j)][3]=read();
Q=read();
for(int i=1;i<=Q;i++) q[i].x1=read(),q[i].y1=read(),q[i].x2=read(),q[i].y2=read(),q[i].id=i;
solve(1,1,n,m,1,Q);
for(int i=1;i<=Q;i++) printf("%d\n",ans[i]);
return 0;
}

「ZJOI2016」小星星

树形 \(dp\) + 容斥原理。

第一次见到对于排列容斥的题,大开眼界。(可能是我太弱)

那么 \(O(n^3)\) 的树形 \(dp\) 还是好打的嘛。

\(Code\ Below:\)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,e[20][20],sta[20],top;
vector<int> G[20];ll dp[20][20],ans; void treedp(int x,int f){
vector<int>::iterator it;
int y;ll now;
for(it=G[x].begin();it!=G[x].end();it++)
if(*it!=f) treedp(*it,x);
for(int i=1;i<=top;i++){
dp[x][i]=1;
for(it=G[x].begin();it!=G[x].end();it++){
y=*it;
if(y==f) continue;
now=0;
for(int j=1;j<=top;j++)
if(e[sta[i]][sta[j]]) now+=dp[y][j];
dp[x][i]*=now;
}
}
} inline void solve(){
treedp(1,0);
for(int i=1;i<=top;i++){
if((n-top)&1) ans-=dp[1][i];
else ans+=dp[1][i];
}
} void dfs(int x){
if(x==n+1){solve();return;}
sta[++top]=x;dfs(x+1);
top--;dfs(x+1);
} int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
e[x][y]=e[y][x]=1;
}
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1);
printf("%lld\n",ans);
return 0;
}

「ZJOI2016」线段树

神仙 \(dp\) 系列。

\(f[x][p][l][r]\) 表示经过 \(p\) 轮操作后 \(\text{max}_{i=l}^{r}{a_i}\leq x<\text{min}(a_{l-1},a_{r+1})\) 的方案数。

那么 \(x\) 可以枚举省掉一维,\(p\) 可以滚动数组,那么实际只有两位。我们先看 \(O(n^4)\) 的解法。

\(f[p][l][r]\) 可以由三项转移:

区间左右端点在 \([1,l-1]\) 或 \([l,r]\) 或 \([r+1,n]\):\(f[p][l][r]=[{l-1\choose 2}+{r-l+1\choose 2}+{n-r\choose 2}]\times f[p-1][l][r]\)

区间右端点在 \(l-1\):\(f[p][l][r]=\sum_{i=1}^{l-1}(i-1)\times f[p-1][i][r]\)

区间左端点在 \(r+1\):\(f[p][l][r]=\sum_{i=r+1}^{n}(n-i)\times f[p-1][l][i]\)

现在看 \(O(n^3)\) 的解法。

不容易发现可以把 \(x\) 一起转移,然后就 \(O(n^3)\) 的解法了。

代码是 \(O(n^4)\) 的。

\(Code\ Below:\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=400+10;
const int mod=1e9+7;
int n,m,a[maxn],mp[maxn],cnt[maxn],C[maxn][maxn],sum[maxn][maxn],f[2][maxn][maxn],g[maxn][maxn];
bool is[maxn][maxn]; /*
cnt[i] = i * (i + 1) / 2
f(i, l, r) += f(i - 1, l, r) * (cnt[r - l + 1] + cnt[n - r] + cnt[l - 1]);
f(i, l, r) += \sum _ {u = 1} ^ {l - 1} f(i - 1, u, r) * (u - 1)
f(i, l, r) += \sum _ {v = r + 1} ^ {n} f(i - 1, l, v) * (n - v)
*/ inline void solve(int x,int L,int R){
memset(f[0],0,sizeof(f[0]));
for(int i=L;i<=R;i++) is[i][a[x]]=1;
f[0][L][R]=1;
int now,lst,val;
for(int i=1;i<=m;i++){
now=i&1;lst=i&1^1;
for(int l=L;l<=R;l++)
for(int r=l;r<=R;r++) f[now][l][r]=f[lst][l][r]*C[l][r]%mod;
for(int r=L;r<=R;r++){
val=0;
for(int l=L;l<=r;l++){
(f[now][l][r]+=val)>=mod?f[now][l][r]-=mod:0;
val=(val+f[lst][l][r]*(l-1))%mod;
}
}
for(int l=L;l<=R;l++){
val=0;
for(int r=R;r>=l;r--){
(f[now][l][r]+=val)>=mod?f[now][l][r]-=mod:0;
val=(val+f[lst][l][r]*(n-r))%mod;
}
}
}
//[L, i] [i, R]
for(int i=L-1;i<=R;i++)
for(int j=L-1;j<=R;j++) sum[i][j]=0;
for(int i=L;i<=R;i++)
for(int j=L;j<=R;j++) sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+f[m&1][i][j])%mod;
for(int i=L;i<=R;i++) g[i][a[x]]=((g[i][a[x]]+sum[i][R]-sum[L-1][R]-sum[i][i-1]+sum[L-1][i-1])%mod+mod)%mod;
} signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),mp[i]=a[i];
sort(mp+1,mp+n+1);
for(int i=1;i<=n;i++) a[i]=lower_bound(mp+1,mp+n+1,a[i])-mp;
for(int i=1;i<=n;i++) cnt[i]=1ll*i*(i+1)/2%mod;
for(int l=1;l<=n;l++)
for(int r=l;r<=n;r++) C[l][r]=(cnt[l-1]+cnt[n-r]+cnt[r-l+1])%mod;
int L,R;
for(int i=1;i<=n;i++){
L=i;R=i;
while(L>1&&a[L-1]<a[i]) L--;
while(R<n&&a[i]>=a[R+1]) R++;
solve(i,L,R);
}
int tmp,val,ans;
for(int i=1;i<=n;i++){
val=0;
for(int j=1;j<=n;j++){
if(!is[i][j]) continue;
tmp=g[i][j];g[i][j]-=val;
if(g[i][j]<0) g[i][j]+=mod;
val=tmp;
}
}
for(int i=1;i<=n;i++){
ans=0;
for(int j=1;j<=n;j++) ans=(ans+g[i][j]*mp[j])%mod;
printf("%lld ",ans);
}
printf("\n");
return 0;
}

「ZJOI2016」大森林

题解戳这里

「ZJOI2016」解题报告的更多相关文章

  1. 「ZJOI2016」大森林 解题报告

    「ZJOI2016」大森林 神仙题... 很显然线段树搞不了 考虑离线操作 我们只搞一颗树,从位置1一直往后移动,然后维护它的形态试试 显然操作0,1都可以拆成差分的形式,就是加入和删除 因为保证了操 ...

  2. 「ZJOI2016」旅行者 解题报告

    「ZJOI2016」旅行者 对网格图进行分治. 每次从中间选一列,然后枚举每个这一列的格子作为起点跑最短路,进入子矩形时把询问划分一下,有点类似整体二分 至于复杂度么,我不会阿 Code: #incl ...

  3. 2090. 「ZJOI2016」旅行者 分治,最短路

    2090. 「ZJOI2016」旅行者 链接 loj 思路 \((l,mid)(mid+1,r)\).考虑跨过mid的贡献. 假设选的中间那条线的点为gzy,贡献为\(dis(x,gzy)+dis(g ...

  4. 「雅礼集训 2017 Day2」解题报告

    「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...

  5. 「雅礼集训 2017 Day1」 解题报告

    「雅礼集训 2017 Day1」市场 挺神仙的一题.涉及区间加.区间除.区间最小值和区间和.虽然标算就是暴力,但是复杂度是有保证的. 我们知道如果线段树上的一个结点,\(max=min\) 或者 \( ...

  6. 「JOI2019 Final」解题报告

    传送门 「JOI2019 Final」勇者比太郎 看懂题就很简单了,后缀和随便维护一下就好了,别用树状数组强加一个\(\log\)就行. 「JOI2019 Final」画展 显然可以先把所有的画框按大 ...

  7. 「THP3考前信心赛」解题报告

    目录 写在前面&总结: T1 T2 T3 T4 写在前面&总结: \(LuckyBlock\) 良心出题人!暴力分给了 \(120pts\) \(T1\) 貌似是个结论题,最后知道怎么 ...

  8. 洛谷 P3349 [ZJOI2016]小星星 解题报告

    P3349 [ZJOI2016]小星星 题目描述 小\(Y\)是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有\(n\)颗小星星,用\(m\)条彩色的细线串了起来,每条细线连着两颗小星星. 有一 ...

  9. 【LOJ】#2090. 「ZJOI2016」旅行者

    题解 每次按较长边把矩形分成两半,找一个中间轴,轴上的每个点跑一边最短路更新所有的答案 然后把矩形分成两半,递归下去 代码 #include <bits/stdc++.h> #define ...

随机推荐

  1. Python播放、关闭音乐代码

    1.安装pygame:win + r :打开控制台输入:pip install pygame 2.#导入 import time import pygame 3.设置音乐绝对路径 #音乐路径 file ...

  2. Linux驱动之触摸屏程序编写

    本篇博客分以下几部分讲解 1.介绍电阻式触摸屏的原理 2.介绍触摸屏驱动的框架(输入子系统) 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍电阻式触摸屏的原理 ...

  3. Image 图片

    随机矩阵画图 这一节我们讲解怎样在matplotlib中打印出图像.这里打印出的是纯粹的数字,而非自然图像.下面用 3x3 的 2D-array 来表示点的颜色,每一个点就是一个pixel. impo ...

  4. echart 图例

    说明:stack相同,两个bar合并但是不会重叠 如果需要重叠 用barGap: '-100%', 根据不同的需求来使用两者. <template> <div> echart ...

  5. Servlet获取 URL 地址

    使用 ServletRequest 的如下方法 getContextPath 取得项目名 getServletPath 取得Servlet名 getPathInfo 取得Servlet后的URL名,不 ...

  6. 关于oracle 索引,收藏

    该篇文章很好,,收藏了.. https://www.cnblogs.com/liangyihui/p/5886619.html oracle 索引建立: create  bitmap/UNIQUE i ...

  7. openwrt添加自动启动项

    在/etc/init.d下添加文件my-plugin #!/bin/sh /etc/rc.common # /etc/init.d/my-plugin start() { sh /root/useri ...

  8. css firefox火狐浏览器下的兼容性问题

    1.DOCTYPE 影响 CSS 处理 2.FF: div 设置 margin-left, margin-right 为 auto 时已经居中, IE 不行 3.FF: body 设置 text-al ...

  9. C#学习笔记14——TRACE、DEBUG和TRACESOURCE的使用以及日志设计

    Trace.Debug和TraceSource的使用以及日志设计   .NET Framework 命名空间 System.Diagnostics 包含用于跟踪执行流程的 Trace.Debug 和 ...

  10. web端常见测试点

    由于web端应用于用户直接相关,又通常需要承受长时间的大量操作,因此web项目的功能和性能都必须经过可靠的验证.web端测试常见的有界面测试.功能测试.性能测试.可用性(接口)测试.兼容性测试.安全性 ...