Picture

题目大意

IOI 1998

求n (<=5000)个矩形 覆盖的图形 的周长(包括洞), 坐标范围[-10000,10000]

题解

一眼离散化+2维线段树,但仔细一想 空间不太够,时间勉强接受

然后目测可能1维线段树+扫描线了?

然后 竟然 裸的扫描线可以过,如下面代码

总数量级上来讲,输入O(n),排序O(n log n),扫描过程O(sum(len周长))5000*20000*4的上限[ 不过USACO给过了,

所以还是线段树好?

从实现来讲,把矩形拆分成x和y方向,靠计算每个块的累计层数 来判断边界

#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--) using namespace std; const string filename = "picture"; void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
} int N,ans=0;
// OX向和OY向的 线段分离处理,互不影响
tuple<int,bool,int,int> Lx[10010],Ly[10010]; // {位置idx坐标,结束边bool?,st->end}
int level[20010]; void Scan(tuple<int,bool,int,int> *L) {
sort(L,L+N);
rep(i,0,20001)
level[i]=0;
rep(i,0,N){
rep(j,get<2>(L[i]),get<3>(L[i])){
if (!get<1>(L[i])){
ans += level[j+10000]==0;
level[j+10000]++;
} else {
level[j+10000]--;
ans += level[j+10000]==0;
}
}
}
} int main(){
usefile();
scanf("%d",&N);
rep(i,0,N){
int x1,x2,y1,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
// OX 方向边
Lx[i*2] = {y1,false,x1,x2};
Lx[i*2+1] = {y2,true,x1,x2};
// OY 方向边
Ly[i*2] = {x1,false,y1,y2};
Ly[i*2+1] = {x2,true,y1,y2};
}
N*=2;
Scan(Lx);
Scan(Ly);
printf("%d\n",ans);
return 0;
}

Hidden Password

ACM South Eastern Europe -- 2003

题目大意

字符串L(长度<=100’000)

求 以该字符串,平移后的, 所有字符串中 字典序最小的字符串的首字母在原字符串中的下标

如cba

所有字符串 acb bac cab, (排序后),第一个字符串首字母对应原来字符串位置为2 (从0计数)

题解

枚举!

这样 我们首先找到最小字母 扫一遍O(n)

然后找到这些开始的坐标

逐步增加长度

那么我们由递归关系,保证每次增加长度后,当前还剩余的坐标只有最小的留下,

因此当增加长度到l时,我们 的维持的起始坐标是 整个字符串里,长度从1l都是字典序最小的

那么我们有两种长度改变方式

  1. +1

假设所有点按照长度扩充 都不属于当前维护的点,那么,长度加1,保留,增加字符最小的点

例子 abcabdzzzabc

初始所有a的坐标[0,3,9],长度1

扩充,扩充目标分别为[1,4,10],都不是当前维护的点([0,3,9])

所以比较元素,全为b,长度=1+1=2

接下来,扩充目标为[2,5,11],也都不是维护的点

比较字符,两个abc,一个abd,所以维护的点变为[0,9],长度变为=2+1=3

再扩充,扩充目标为[3,0],注意到0是我们当前维护的[0,9]中的元素,所以不采取+1的方案

  1. 倍增

假设字符aabbabbb,

那么在找完最小字符后,起始坐标还剩下[0,1,4],一旦发现任意一个扩充的下一步([1,2,5]) 是一个维持的点,那么长度翻倍,后一个点删除,在这种情况下,扩充的位置不是最小坐标的点直接移除。

因为我们维持的点 == 从1到该长度下,每个长度 都是字典序最小的,所以没有在维护中的点,都是非字典序最小的,所以 可以倍增

删除右边的点是因为 扩充右边的维护点的字典序一定大于等于 左边的点,等于的情况由判环处理

如上在一次扩充后发现0的下一个扩充是1,而1是我们维持着的点,所以长度=1*2,1点删除,4扩充是5,那么5没有被维持,所以4点也被删除,综上最后剩下0

以上描述存在的问题:起始点是哪个点?

假设字符串 aaazzaaza,

显然在初始操作后 需要维护的点有[0,1,2,5,6,8]

注意到,如果从左向右来处理,按照上面的算法会 变成[0,6,8???],而实际 从环的性质来看,期望的应该是得到[1,6,8],也就是8位置的看做[8,0,1,2]这一段的起始点。

这里加一个父节点的查找,找到环意义上该点所对应的最左的点即可,在下方函数看的话就是circle,

同时,circle这里如果发现,整个保存的点构成了环,那么也就是 这些点仅对于环上字典序的等价了,根据题目期望这种情况下最小index,就取出即是答案


空间复杂度,emmmm没啥好说,看变量即可,维持在O(n)

时间复杂度,

每一次倍增会让点数至少除以2,因为一个点要留下来,那么首先它的扩展点要在原来的维护里,并且下一次维护需要消失,所以每次要留一个点,就一定要删一个点,还有的点不会被留下,所以留下的一定小于等于上一次的一半

O(n+n/2+n/4) = O(2n) = O(n)

考虑对任意长度,都是执行+1,那么每次能执行+1的限度为

sum(n*(1+1/2+...1/n))

众所周知这是一个无穷级数,所以时间复杂度无穷大

大自也是O(12n)=O(n)的复杂度,

下面就是实际上,是这两种穿插 ,那么一定小于等于O(2n+12n)=O(n), (数学的习惯懒得分类讨论不等的情况 能用就行,所以留一个等于)

综上 时间空间均满足

char s[100010];
int sz=0; bool pos[100010];
vector<int>p;
vector<int>q;
int L; bool circle(int idx,int len,int &newidx){
newidx = (idx+L-len)%L;
while(newidx != idx){
if(!pos[newidx]){
(newidx+=len)%=L;
return false;
}else{
newidx = (newidx+L-len)%L;
}
}
while(newidx - L > 0){
newidx -= L;
}
printf("%d\n",newidx);
return true;
} int main(){
usefile();
// 同步增长,冲突取前,倍增 其余删除(因为保证最小)
scanf("%d",&L);
while(sz<L){
scanf("%s",s+sz);
sz+=strlen(s+sz);
}
char minch = s[0];
rep(i,1,L){
minch = min(minch,s[i]);
}
rep(i,0,L){
if(s[i] == minch){
p.push_back(i);
pos[i]=true;
}
}
int l = 1;
while(p.size() > 1){
int state = 0; // 0 for +1, 1 for *2
minch = s[(p[0]+l)%L];
for(auto idx : p){
if(pos[(idx+l)%L] == true){
state = 1;
break;
}
minch = min(minch,s[(idx+l)%L]);
}
if(state == 0){
q.clear();
for(auto idx:p){
if(!pos[idx])continue;
if(s[(idx+l)%L] == minch){
q.push_back(idx);
}else{
pos[idx]=false;
}
}
p=q;
l++;
}else{
q.clear();
int startidx ;
int ret = circle(p[0],l,startidx);
if(ret){
return 0;
}
int pidx = 0;
for(pidx=0;pidx<p.size();pidx++){
if(p[pidx] == startidx){
break;
}
}
rep(i,pidx,p.size()){
int idx = p[i];
if(!pos[idx])continue;
if(pos[(idx+l)%L]){
q.push_back(idx);
pos[(idx+l)%L] = false;
}else{
pos[idx]=false;
}
}
rep(i,0,pidx){
int idx = p[i];
if(!pos[idx])continue;
if(pos[(idx+l)%L]){
q.push_back(idx);
pos[(idx+l)%L] = false;
}else{
pos[idx]=false;
}
}
p=q;
l*=2;
}
}
printf("%d\n",p[0]);
return 0;
}

Twofive

题目大意

IOI 2001

A到Y构成的排列,满足 把这25个字母排成 5*5矩阵后 每行每列,单调递增,则为合法的

所有合法的排列,按照字典序 排序

请编写 字典序序号 到字符串 和 字符串反向转换为字典序序号的程序

尝试思路

看数据量,我自己也估计不到实际大小于是先写了一个打表,(上界 是25!但是有限制所以不知道是否会降低

#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--) using namespace std; const string filename = "twofive"; void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
} int chars[30];
int vis[30];
int cnt = 0; void print(){
cout<<endl;
rep(i,0,5){
rep(j,0,5){
cout<<char(chars[i*5+j]+'a')<<" ";
}
cout<<endl;
}
} void gen(int idx){
if(idx%5==0){
rep(i,0,25){
if(vis[i] == 0){
vis[i]=1;
chars[idx] = i;
gen(idx+1);
vis[i]=0;
return ;
}
}
}
if(idx == 24){
cnt++;
chars[24]=24;
print();
return ;
}
int sti = chars[idx-1];
if(idx>5){
sti=min(sti,chars[idx-5]);
} rep(i,sti+1,26-(5-idx%5)*(5-idx/5)){
if(vis[i])continue;
vis[i]=1;
chars[idx] = i;
gen(idx+1);
vis[i]=0;
}
} int main(){
// usefile();
gen(0);
cout<<cnt<<endl; return 0;
}

跑了一下大概

7951237
a b c d e
f g h l n
i m k q w
j p o r u
s t v x y

以上的改动才到i,说明 数量级上,不可能期望打表了

接下来,注意到,如果我们有方法从数字序号转换到 对应的单词,那么 可以2分法 找到对应的单词

同理,如果我们找到 单词映射到序号的方法,那么2分(如果可以,因为这里二分单词似乎没那么好做)也能反过来找数字,所以分析上,任何一个问题 都可以解决对应的一个

还有个思路是,简单的排序,计算被删掉的个数,那么序列= 总排序-被删掉比它小的个数

题解

记忆化+dp

我们如果把数从小到大填入

那么显然,新插入的数在已经插入的数的行末,也在已经插入的数的列末,如

a b e
c d f
g
h
i

j可插入的位置 为g右侧e右侧

所以 我们有dp

dp[i0][i1][i2][i3][i4] 表示 第0行i0个,第1行i1个数...第i4行个数的情况下,剩余未填部分的期望个数

不考虑具体,仅考虑题目基本限制的情况下, 满足 ij >= i(j+1),因为我们按照顺序放数字,所以上面的行的个数不小于下一行

有转移方程

dp[i0][i1][i2][i3][i4] = dp[i0-1][...]+dp[...][i1-1][...]+dp[...][i2-1][...]+dp[...][i3-1][...]+dp[...][i4-1]

其中 如果在-1时不满足 行之间个数的大小关系,那么 对应的dp直接为0

综上,我们有了在没有 具体求值下,能计算所有满足题目限制下的dp,时间复杂度 = O(空间*状态转移)=O(6^5*5),空间O(6^5)

求解

接下来是如何进行转换的问题求

因为所求的idx为实际的 合法twofive的字典序

那么我们可以按位逼近,//延伸阅读 BROP的按位枚举攻击方法

假设我们求的是

ADEFGBHIJKC...Y, 那么 它 的字典序 = AB...的所有+AC...的所有+...

简单的说,如果一个前缀小于 要求的等长前缀,那么加上该前缀的所有个数,

如果该前缀等于要求的值的等长前缀,那么前缀长度+1

外层for前缀长度,中间for字母, 时间复杂度小于 O(25*25)

以上我们可以 从 字符串转化为index

相反

同样用逼近的方法,可以O(25*25)时间复杂度内 index转化为 字符串

#include <bits/stdc++.h>
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--) using namespace std; const string filename = "twofive"; void usefile(){
freopen((filename+".in").c_str(),"r",stdin);
freopen((filename+".out").c_str(),"w",stdout);
} char s[100]; // 按位逼近的
char str[100]; // str2index int dp[6][6][6][6][6]; int dfs(int a=0, int b=0, int c=0, int d=0, int e=0, char ch='A') {
if(ch > 'Y') return 1;
int &ret = dp[a][b][c][d][e];
if(ret) return ret;
// 每一行 一定小于等于上一行
int w = 5;
int *v[6]={&w,&a,&b,&c,&d,&e};
rep(i,1,6){
// 未填过 和 已经填过(按照 字母顺序扩展)
int idx = *v[i]+(i-1)*5;
if(*v[i] < *v[i-1] && (s[idx] == 0 || s[idx] == ch)){
(*v[i])++;
ret+=dfs(a,b,c,d,e,ch+1);
(*v[i])--;
}
}
return ret;
} void index2word(){
int n;
scanf("%d",&n);
rep(i,0,25){
for(s[i] = 'A';; s[i]++) { // 按位逼近 时间复杂度25×25
memset(dp, 0,sizeof(dp));
int ret = dfs();
// cout<<i<<" = "<<s[i]<<"\tret = "<<ret<<endl;
if(ret >= n) break;
n -= ret;
}
}
printf("%s\n", s);
} void word2index(){
scanf("%s", str);
int ans = 1;
rep(i, 0, 25) {
for(s[i] = 'A'; s[i] < str[i]; s[i]++) {
memset(dp, 0,sizeof(dp));
ans += dfs();
}
}
printf("%d\n", ans);
} int main(){
usefile();
char c;
cin >> c;
if(c == 'N') { // index 2 word
index2word();
} else if(c == 'W') { // word 2 index
word2index();
}
return 0;
}

上面实现中 dp过程是按照字母顺序填写,由ch保证,所以在最外层枚举dp的时候,就直接从A 到Y 了

USACO 5.5 章节的更多相关文章

  1. USACO 6.4 章节

    The Primes 题目大意 5*5矩阵,给定左上角 要所有行,列,从左向右看对角线为质数,没有前导零,且这些质数数位和相等(题目给和) 按字典序输出所有方案... 题解 看上去就是个 无脑暴搜 题 ...

  2. USACO 6.3 章节 你对搜索和剪枝一无所知QAQ

    emmm........很久很久以前 把6.2过了 所以emmmmmm 直接跳过 ,从6.1到6.3吧 Fence Rails 题目大意 N<=50个数A1,A2... 1023个数,每个数数值 ...

  3. USACO 5.4 章节

    Canada Tour 题目大意 双向连通图,点从左向右排列, 你需要先从最左的点到最右的点,(过程中只能从左向右走) 然后再从最右的点返回最左的点,(过程中只能从右向左走) 过程中除了最左的点,其它 ...

  4. USACO 5.3 章节

    相关讲解可在USACO上看原文,也可以搜索nocow找到翻译的! (nocow上有些微翻译是有问题的,如果想看nocow翻译的建议也对着英文看) 以下记录以下 自己之前未掌握的一些要点,以及按自己的括 ...

  5. USACO 6.5 章节 世界上本没有龙 屠龙的人多了也便有了

    All Latin Squares 题目大意 n x n矩阵(n=2->7) 第一行1 2 3 4 5 ..N 每行每列,1-N各出现一次,求总方案数 题解 n最大为7 显然打表 写了个先数值后 ...

  6. USACO 6.1 章节

    Postal Vans 题目大意 4*n的网格,要经过所有点的有向有环,不重复经过点的路径总数 n<=1000 题解 显然 插头dp 以4为切面 问题是,会发现 超精度 解决呢要么实现高精度,要 ...

  7. (Step1-500题)UVaOJ+算法竞赛入门经典+挑战编程+USACO

    http://www.cnblogs.com/sxiszero/p/3618737.html 下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年 ...

  8. 算法竞赛入门经典+挑战编程+USACO

    下面给出的题目共计560道,去掉重复的也有近500题,作为ACMer Training Step1,用1年到1年半年时间完成.打牢基础,厚积薄发. 一.UVaOJ http://uva.onlinej ...

  9. USACO . Your Ride Is Here

    Your Ride Is Here It is a well-known fact that behind every good comet is a UFO. These UFOs often co ...

随机推荐

  1. mysql DATETIME和TIMESTAMP类型

    以mysql 5.7.20 为例 一直以来,理解有偏差,作此记录,纠正 一.DATETIME和TIMESTAMP 都有高达微秒(6位)的精度 范围   DATETIME  1000-01-01 00: ...

  2. Codeforces 375D D. Tree and Queries

    传送门 题意: 给一棵树,每个节点有一个颜色,询问x为根的子树,出现次数大于等于k的颜色个数. 输入格式: 第一行 2 个数 n,m 表示节点数和询问数. 接下来一行 n 个数,第 i 个数 ci ​ ...

  3. XSLT学习(九)通过JavaScript转化xml

    如果您的浏览器支持 XSLT,那么在浏览器中它可被用来将文档转换为 XHTML. JavaScript 解决方案 在前面的章节,我们已向您讲解如何使用 XSLT 将某个 XML 文档转换为 XHTML ...

  4. maven私服nexus3.9安装配置

    maven私服nexus3.9安装配置 私服介绍 私服是指私有服务器,是架设在局域网的一种特殊的远程仓库,目的是代理远程仓库及部署第三方构建.有了私服之后,当 Maven 需要下载构件时,直接请求私服 ...

  5. Python Web开发:使用Django框架创建HolleWorld项目

    开发环境搭建 Python环境安装 下载地址:https://www.python.org/downloads// Django安装 打开Windows CMD输入pip install django ...

  6. Java疯狂讲义笔记——枚举类

    枚举类 ——Java5[基础知识]1,定义枚举类——关键字 enum (地位与class.interface相同).2,枚举类是一个特殊的类,可以有成员变量.方法,实现一个或多个接口,定义自己的构造器 ...

  7. Codeforces Round #392 (Div. 2) - A

    题目链接:http://codeforces.com/contest/758/problem/A 题意:给定N个城市的福利,国王现在想让每个城市的福利都一致.问最少需要花多少钱使得N个城市的福利值都一 ...

  8. rabbit 独占队列

    std::string queue_name = "hello"; AmqpClient::Channel::ptr_t channel = AmqpClient::Channel ...

  9. Python3.5-20190501-廖老师的

    python是一门解释型\脚本语言(和js特别像,如果同时学习js和python完全搅浑了.) 在运行py时候是一句一句翻译成cpu识别的机器码,所以速度比较慢.而C程序是运行前直接编译成CPU能执行 ...

  10. JDBC简单总结

    几种常用数据库的JDBC URL 对于 Oracle 数据库连接,采用如下形式: jdbc:oracle:thin:@localhost:1521:sid 对于 SQLServer 数据库连接,采用如 ...