Codeforces 627E - Orchestra(双向链表,思维题)
下设 \(n,m\) 同阶。
首先有一个傻子都会的暴力做法,枚举矩形的上、下边界 \(l,r\),考虑集合多重集 \(S=\{y|x\in[l,r],(x,y)\text{为标记点}\}\),也就是所有在第 \(l\) 行与第 \(r\) 行之间所有标记点的列坐标的集合,考虑进一步枚举矩形的左端点 \(x\),那么对于右端点 \(y\),以 \((l,x)\) 为左上角,\((r,y)\) 为右下角的矩形中至少有 \(k\) 个标记点当前仅当 \(S\) 中有至少 \(k\) 个元素 \(\in[x,y]\),故考虑用 two pointers 求出这个 \(y\),时间复杂度 \(n^3\)。
考虑优化这个过程,注意到 \(k\) 最多只有 \(10\),因此考虑从此下手。我们将 \(S\) 中的元素从小到大排个序,记作 \(x_1,x_2,\cdots,x_{|S|}\),记 \(nxt_x\) 为当矩形的左边界为 \(x\) 时候最小的右边界 \(y\) 使得 \(S\) 中至少有 \(k\) 个元素 \(\in[x,y]\),那么显然 \(\forall v\in(x_{i-1},x_i],nxt_v=x_{i+k-1}\),因此 \(i\) 会对答案产生 \((x_i-x_{i-1})\times(m-x_{i+k-1})\) 的贡献,我们再记 \(f_i=(x_i-x_{i-1})\times(m-x_{i+k-1})\),那么显然矩形矩形的上、下边界分别为 \(l,r\) 时的答案就是 \(\sum\limits_{i=1}^{|S|}f_i\)。
我们考虑还是枚举矩形的上边界、下边界,并在枚举的过程中动态维护 \(f_i\) 的变化。注意到你每加入/删除一个点,最多会影响 \(k+1\) 个 \(f_i\)。因此每次加入/删除时暴力修改 \(f_i\) 都没问题。那么现在问题就来了,怎么修改 \(f_i\) 呢?一个很显然的思路是 std::multiset<int>,不过非常抱歉,std::multiset<int> 会平白多一个 \(\log\),复杂度 \(n^2k\log n\),再加上 set 的大常数,无法通过此题。注意到每次修改我们只用找出前驱、后继,因此考虑使用擅长 \(\mathcal O(1)\) 求出前驱、后继的双向链表,这样就可以把 set 的 \(\log\) 去掉了。还有一点,就是双向链表不支持随机访问,因此使用双向链表动态加点是实现不了了。故考虑换个思路,既然双向链表不支持动态加点,我们就考虑动态删点,首先将横坐标 \(\ge l\) 的点按照纵坐标从小到大的顺序加入双向链表,然后每扫过一行就将这一行中的标记点从双向链表中删除即可。复杂度 \(n^2k\)。
细节有亿点点多,大约也就调了 2h 罢(((
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=3e3;
int R,C,n,k,x[MAXN+5],y[MAXN+5];
vector<int> vr[MAXN+5],vc[MAXN+5];
int l[MAXN+5],r[MAXN+5];ll ans=0;
int main(){
scanf("%d%d%d%d",&R,&C,&n,&k);
for(int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
vr[x[i]].pb(i);vc[y[i]].pb(i);
}
for(int i=1;i<=MAXN;i++) sort(vr[i].begin(),vr[i].end(),[](int lhs,int rhs){return y[lhs]>y[rhs];});
for(int i=1;i<=MAXN;i++) sort(vc[i].begin(),vc[i].end(),[](int lhs,int rhs){return x[lhs]<x[rhs];});
for(int i=1;i<=R;i++){
memset(l,0,sizeof(l));memset(r,0,sizeof(r));
int pre=0;
for(int j=1;j<=C;j++) for(int t=0;t<vc[j].size();t++)
if(x[vc[j][t]]>=i){
int id=vc[j][t];l[id]=pre;
if(pre) r[pre]=id;pre=id;
}
ll ret=0;
for(int j=1;j<=C;j++) for(int t=0;t<vc[j].size();t++)
if(x[vc[j][t]]>=i){
int id=vc[j][t],cur=id;
for(int stp=1;stp<k;stp++) cur=r[cur];
// printf("%d %d\n",id,cur);
if(cur) ret+=1ll*(C-y[cur]+1)*(y[id]-y[l[id]]);
}
// printf("%d %d %lld\n",i,R,ret);
ans+=ret;
for(int j=R;j>=i;j--){
for(int t=0;t<vr[j].size();t++){
int id=vr[j][t];
if(!r[id]){
int cur=id;
for(int stp=1;stp<k;stp++) cur=l[cur];
if(cur) ret-=1ll*(y[cur]-y[l[cur]])*(C-y[id]+1);
if(r[id]) l[r[id]]=l[id];
if(l[id]) r[l[id]]=r[id];
continue;
}
int cr=r[id],cl=r[id],stp=0;
while(stp<k-1){if(!r[cr]) break;cr=r[cr];++stp;}
for(int o=1;o<=k-1-stp;o++) cl=l[cl];
if(cl) ret-=1ll*(C-y[cr]+1)*(y[cl]-y[l[cl]]);
for(int o=1;o<=stp+1;o++){
cr=l[cr];cl=l[cl];
if(cl) ret-=1ll*(C-y[cr]+1)*(y[cl]-y[l[cl]]);
}
if(r[id]) l[r[id]]=l[id];
if(l[id]) r[l[id]]=r[id];
if(!r[id]) continue;
cr=r[id],cl=r[id],stp=0;
while(stp<k-1){if(!r[cr]) break;cr=r[cr];++stp;}
for(int o=1;o<=k-1-stp;o++) cl=l[cl];
if(cl) ret+=1ll*(C-y[cr]+1)*(y[cl]-y[l[cl]]);
for(int o=1;o<=stp;o++){
cr=l[cr];cl=l[cl];
if(cl) ret+=1ll*(C-y[cr]+1)*(y[cl]-y[l[cl]]);
}
} ans+=ret;
// printf("%d %d %lld\n",i,j-1,ret);
}
} printf("%lld\n",ans);
return 0;
}
/*
10 10 10 4
7 9
8 1
5 7
6 8
6 4
5 8
4 3
1 3
5 1
6 9
4 4 3 1
1 3
2 2
3 4
5 5 5 2
1 2
1 5
3 4
4 3
5 2
*/
Codeforces 627E - Orchestra(双向链表,思维题)的更多相关文章
- CF--思维练习-- CodeForces - 215C - Crosses(思维题)
ACM思维题训练集合 There is a board with a grid consisting of n rows and m columns, the rows are numbered fr ...
- Codeforces 675C Money Transfers 思维题
原题:http://codeforces.com/contest/675/problem/C 让我们用数组a保存每个银行的余额,因为所有余额的和加起来一定为0,所以我们能把整个数组a划分为几个区间,每 ...
- Codeforces 1090D - Similar Arrays - [思维题][构造题][2018-2019 Russia Open High School Programming Contest Problem D]
题目链接:https://codeforces.com/contest/1090/problem/D Vasya had an array of n integers, each element of ...
- codeforces 1140D(区间dp/思维题)
D. Minimum Triangulation time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- Codeforces 957 水位标记思维题
A #include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #def ...
- ACM思维题训练 Section A
题目地址: 选题为入门的Codeforce div2/div1的C题和D题. 题解: A:CF思维联系–CodeForces -214C (拓扑排序+思维+贪心) B:CF–思维练习-- CodeFo ...
- codeforces ~ 1009 B Minimum Ternary String(超级恶心的思维题
http://codeforces.com/problemset/problem/1009/B B. Minimum Ternary String time limit per test 1 seco ...
- 贪心/思维题 Codeforces Round #310 (Div. 2) C. Case of Matryoshkas
题目传送门 /* 题意:套娃娃,可以套一个单独的娃娃,或者把最后面的娃娃取出,最后使得0-1-2-...-(n-1),问最少要几步 贪心/思维题:娃娃的状态:取出+套上(2),套上(1), 已套上(0 ...
- C. Nice Garland Codeforces Round #535 (Div. 3) 思维题
C. Nice Garland time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...
随机推荐
- javascript-jquery选择器
jquery选择器用来获得jquery对象 我们用一个实例来演示jquery与原生的区别 <div id="title">123</div>原生获得元素的方 ...
- PHP文件上传漏洞与一句话木马
靶子代码: 前端效果: 这是个没有任何防护的文件上传代码,同时还热心的附上了上传文件的路径. 我们写好php木马后,什么额外工作也不需要做,直接上传就行了.上传后在浏览器里访问该文件,其就会被执行. ...
- vue3.x全局插件和组件
做vue项目的时候,总有一些小组件或者工具类,我们需要频繁的使用,每个使用的地方再去引用相对比较麻烦,当然也有一些好处,尤其是配合组件异步加载的时候,能最更好的减少项目首次加载的体积,从而优化一些体验 ...
- Linux零基础之shell基础编程入门
从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操 ...
- 数据治理之元数据管理的利器——Atlas入门宝典
随着数字化转型的工作推进,数据治理的工作已经被越来越多的公司提上了日程.作为Hadoop生态最紧密的元数据管理与发现工具,Atlas在其中扮演着重要的位置.但是其官方文档不是很丰富,也不够详细.所以整 ...
- Oracle 11g 新建用户
create user XXXuser identified by XXXpassword;--创建用户XXXuser,设置初始密码XXXpassword alter user XXXuser ide ...
- 另类加法 牛客网 程序员面试经典 C++ Python
另类加法 牛客网 程序员面试经典 C++ Python 题目描述 请编写一个函数,将两个数字相加.不得使用+或其他算数运算符. 给定两个int A和B.请返回A+B的值 测试样例: 1,2 返回:3 ...
- 链式A+B 牛客网 程序员面试金典 C++ Python
链式A+B 牛客网 程序员面试金典 C++ Python 题目描述 有两个用链表表示的整数,每个结点包含一个数位.这些数位是反向存放的,也就是个位排在链表的首部.编写函数对这两个整数求和,并用链表形式 ...
- Codeforces Round #747 (Div. 2)题解
谢天谢地,还好没掉分,还加了8分,(8分再小也是加啊)前期刚开始有点卡,不过在尽力的调整状态之后,还是顺利的将前面的水题过完了,剩下的E2和F题就过不去了,估计是能力问题,自己还是得认真补题啦. E2 ...
- 『学了就忘』Linux基础命令 — 25、文件基本权限的管理
目录 1.文件和目录的默认权限 2.umask默认权限 (1)查看系统的umask权限 (2)用八进制数值显示umask权限 (3)umask权限的计算方法 (4)注意:umask默认权限的计算绝不是 ...