bzoj 4822~4824 CQOI2017题解
老C的任务
题目大意:
维护一个二维平面,初始给出一些点及其权.多次询问某个矩形内的权和.
n,m <= 100000
题解:
签到题.
CDQ水一水.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;static char ch;static bool flag;flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=(x<<1)+(x<<3)+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 100010;
const int maxm = maxn;
struct Node{
int x,y;
int type,val,id;
Node(){}
Node(const int &a,const int &b,
const int &c,const int &d,const int &e){
x = a;y = b;type = c;val = d;id = e;
}
}q[maxn*6];
#define lowbit(x) (x&-x)
int n,m,mxlim;
ll c[maxn*6];int vis[maxn*6],T;
inline void modify(int x,ll d){
for(;x <= mxlim;x += lowbit(x)){
if(vis[x] == T) c[x] += d;
else c[x] = d,vis[x] = T;
}
}
inline ll query(int x){
ll ret = 0;
for(;x;x-=lowbit(x)) if(vis[x] == T) ret += c[x];
return ret;
}
int qcnt = 0;ll ans[maxm];
void solve(int l,int r){
if(l == r) return ;
int mid = l+r >> 1;
solve(l,mid);solve(mid+1,r);
++ T;rg i = l,j = mid+1,k = l;
static Node tmp[maxn*6];
while(i <= mid || j <= r){
if(i > mid || (j <= r && q[j].x < q[i].x)){
if(q[j].type == 2){
ans[q[j].id] += query(q[j].y)*q[j].val;
}
tmp[k++] = q[j++];
}else{
if(q[i].type == 1) modify(q[i].y,q[i].val);
tmp[k++] = q[i++];
}
}
rep(i,l,r) q[i] = tmp[i];
return ;
}
struct num{
int x,y,val;
}p[maxn];
struct number{
int x1,y1,x2,y2;
}nu[maxm];
int b[maxn*6],cnt;
int main(){
read(n);read(m);
rg x,y,v;
rep(i,1,n){
read(p[i].x);read(p[i].y);read(p[i].val);
b[++cnt] = p[i].y;
}
rg xx,yy;
rep(i,1,m){
read(x);read(y);read(xx);read(yy);
b[++cnt] = y;b[++cnt] = yy;
if(x > xx) swap(x,xx);
if(y > yy) swap(y,yy);
nu[i].x1 = x;nu[i].y1 = y;
nu[i].x2 = xx;nu[i].y2 = yy;
}
sort(b+1,b+cnt+1);
rep(i,1,n){
p[i].y = lower_bound(b+1,b+cnt+1,p[i].y) - b;
x = p[i].x;y = p[i].y;v = p[i].val;
q[++qcnt] = Node(x,y,1,v,0);
}
rep(i,1,m){
y = lower_bound(b+1,b+cnt+1,nu[i].y1) - b;
yy = lower_bound(b+1,b+cnt+1,nu[i].y2) - b;
x = nu[i].x1;xx = nu[i].x2;
q[++qcnt] = Node(xx,yy,2,1,i);
q[++qcnt] = Node(x-1,yy,2,-1,i);
q[++qcnt] = Node(xx,y-1,2,-1,i);
q[++qcnt] = Node(x-1,y-1,2,1,i);
}
mxlim = cnt;
solve(1,qcnt);
rep(i,1,m){
printf("%lld\n",ans[i]);
}
return 0;
}
题目: 老 C 的方块
题目大意:
给定一个这样的R行C列网格图:加粗边称为特殊边
定义若出现下列情况本质相同的情况则不合法.
现在给定网格图内哪些格子是存在的及其被删除的代价共n个
最小化使得网格图合法的代价.
R,C,n <= 100000
题解:
观察所有不合法的状态.
我们发现其实就是特殊边两端不能都有大小超过2的四连块.(不经过特殊边)
所以我们可以列出一些关系:
如果将题目中给出的网格图的行列看成横纵坐标轴:(即(1,1)和(2,1)之间为特殊边)
我们可以得到这样的关系:
对于特殊边(3,2) - (4,2)来说.
如果要这条特殊边合法,那么必须是两边不能同时含有大小超过2的四连块.
那么如果我们删除(3,2)或者(4,2)这是一定满足的.
下面就是不删除(3,2)或者(4,2)的情况,那么我们知道:
要么全部删除(3,1),(2,2),(3,3),要么全部删除(4,1),(5,2),(4,3).
然后我们发现我们可以对网格图进行三色染色:建立最小割模型.
假设我们将(3,1),(2,2),(3,3)染成黑色那么(4,1),(5,2),(4,3)一定是白色.
然后将所有在特殊边两边的点染成黄色.
可以建立最小割模型:
蓝色边为删除对应格子的代价,黑色均为inf,黄色边为特殊边两边的各自的代价的最小值.
跑最小割即可.
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!'); if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
typedef pair<int,int> pa;
const int maxn = 100010;
const int inf = 0x3f3f3f3f;
struct Edge{
int to,next,cap;
}G[maxn*10];
int head[maxn],cnt = 1;
void add(int u,int v,int c){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
G[cnt].cap = c;
}
inline void insert(int u,int v,int c){
add(u,v,c);add(v,u,0);
}
#define v G[i].to
int dis[maxn],q[maxn],l,r,S,T;
bool bfs(){
memset(dis,-1,sizeof dis);
l = 0;r = -1;q[++r] = S;
dis[S] = 0;
while(l <= r){
int u = q[l++];
for(int i = head[u];i;i=G[i].next){
if(dis[v] == -1 && G[i].cap != 0){
dis[v] = dis[u] + 1;
q[++r] = v;
}
}
}return dis[T] != -1;
}
int dfs(int u,int f){
if(u == T || f == 0) return f;
int ret = 0;
for(int i = head[u];i;i=G[i].next){
if(dis[v] == dis[u] + 1 && G[i].cap){
int x = dfs(v,min(f,G[i].cap));
f -= x;ret += x;
G[i].cap -= x;
G[i^1].cap += x;
if(f == 0) break;
}
}return ret;
}
inline int dinic(){
int ret = 0;
while(bfs()) ret += dfs(S,inf);
return ret;
}
#undef v
inline bool ca(int x,int y){
int id = (x + 1) >> 1;
if(id & 1) return (y&1) == 1;
else return (y & 1) == 0;
}
inline bool cb(int x,int y){
int id = (x + 1) >> 1;
if(id & 1){
if((y & 1) == 1) return false;
return (x & 1) == 1;
}else{
if((y & 1) == 0) return false;
return (x & 1) == 0;
}
}
inline bool cc(int x,int y){
int id = (x + 1) >> 1;
if(id & 1){
if((y & 1) == 1) return false;
return (x & 1) == 0;
}else{
if((y & 1) == 0) return false;
return (x & 1) == 1;
}
}
int d1[3][2] = {{-1,0},{0,1},{0,-1}};
int d2[3][2] = {{1,0},{0,1},{0,-1}};
map<pa,int>mp;
struct Node{
int x,y,w;
}p[maxn];
int main(){
int C,R,n;read(C);read(R);read(n);
S = maxn - 5;T = S + 1;
rep(i,1,n){
read(p[i].x);read(p[i].y);read(p[i].w);
mp[make_pair(p[i].x,p[i].y)] = i;
if(cb(p[i].x,p[i].y)) insert(S,i,p[i].w);
if(cc(p[i].x,p[i].y)) insert(i,T,p[i].w);
}
rep(i,1,n){
if( (p[i].x & 1) && ca(p[i].x,p[i].y)
&& (mp.count(make_pair(p[i].x+1,p[i].y)))){
rg j = mp[make_pair(p[i].x+1,p[i].y)];
if(p[i].y & 1){
insert(i,j,min(p[i].w,p[j].w));
rep(k,0,2){
rg nx = p[i].x + d1[k][0];
rg ny = p[i].y + d1[k][1];
if(!mp.count(make_pair(nx,ny))) continue;
insert(mp[make_pair(nx,ny)],i,inf);
}
rep(k,0,2){
rg nx = p[j].x + d2[k][0];
rg ny = p[j].y + d2[k][1];
if(!mp.count(make_pair(nx,ny))) continue;
insert(j,mp[make_pair(nx,ny)],inf);
}
}else{
insert(j,i,min(p[i].w,p[j].w));
rep(k,0,2){
rg nx = p[i].x + d1[k][0];
rg ny = p[i].y + d1[k][1];
if(!mp.count(make_pair(nx,ny))) continue;
insert(i,mp[make_pair(nx,ny)],inf);
}
rep(k,0,2){
rg nx = p[j].x + d2[k][0];
rg ny = p[j].y + d2[k][1];
if(!mp.count(make_pair(nx,ny))) continue;
insert(mp[make_pair(nx,ny)],j,inf);
}
}
}
}
int ans = dinic();
printf("%d\n",ans);
return 0;
}
老 C 的键盘
题目大意:
有一个n的排列对于每个位置i,给出[i/2]与i位置上的数的大小关系,求可能的排列数.
n <= 100
题解:
不难发现实际上这是一棵树.
我们把这棵树建出来,那么现在变成一给定一棵树,树上的边代表的父子的大小关系.
我们考虑树归 : 设\(f[i][j]\)表示在以i为根的子树内根作为排名为j的值出现的排列数.
那么问题就在于状态的合并更新了.
对于一个\(f[u][]\)要将儿子的状态\(f[v][]\)合并进来,我们首先分情况讨论:
- u的值必须 > v的值:
我们想象成两个排列进行合并.
我们知道我们必须满足的条件即v在u的前面.
那么我们枚举一下需要有多少排在u的前面即可.
由于需要保证相对顺序不发生变化,所以将n个数插到m个数中的方案即:\(C_{n+m-1}^m\)
但是由于这里要么前面能插,要么后面能插,即什么时候都多一个可以插的位置,
所以实际上这里插入的方案数为\(C_{n+m}^m\)
这样我们列出转移方程:
\]
- u的值必须 < v的值:
相应地列出方程:
\]
预处理前缀后缀和可以将sigma消掉.
据说这么做复杂度是\(O(n^2\log n)\)的..我也不知道为什么..
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;static char ch;static bool flag;flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=(x<<1)+(x<<3)+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 128;
const int mod = (1e9 + 7);
struct Edge{
int to,next,type;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v,int d){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
G[cnt].type = d;
}
int C[maxn][maxn];
inline void prework(int n){
C[0][0] = 1;
rep(i,1,n) rep(j,0,n){
C[i][j] = C[i-1][j];
if(j != 0) C[i][j] += C[i-1][j-1];
if(C[i][j] >= mod) C[i][j] -= mod;
}
}
int f[maxn][maxn],pre[maxn][maxn],suf[maxn][maxn];
inline void init(){
memset(head,0,sizeof head);
cnt = 0;
}
int siz[maxn],n;
#define v G[i].to
void dfs(int u,int fa){
siz[u] = 1;
for(int i = head[u];i;i=G[i].next){
if(v == fa) continue;
dfs(v,u);siz[u] += siz[v];
}
rep(i,2,n) f[u][i] = 0;
f[u][1] = 1;
rg s = 1;
static int g[maxn];
for(int i = head[u];i;i=G[i].next){
if(v == fa) continue;
rep(j,1,s+siz[v]) g[j] = 0;
if(G[i].type == -1){
rep(j,1,s) rep(k,1,siz[v]){
g[j+k-1] += 1LL*C[j+k-2][k-1]*C[s-j+siz[v]-k+1][s-j]%mod*f[u][j]%mod*suf[v][k]%mod;
if(g[j+k-1] >= mod) g[j+k-1] -= mod;
}
}else{
rep(j,1,s) rep(k,1,siz[v]){
g[j+k] += 1LL*C[j+k-1][k]*C[s-j+siz[v]-k][s-j]%mod*f[u][j]%mod*pre[v][k]%mod;
if(g[j+k] >= mod) g[j+k] -= mod;
}
}
rep(j,1,s + siz[v]) f[u][j] = g[j];
s += siz[v];
}
rep(i,1,n){
pre[u][i] = pre[u][i-1] + f[u][i];
if(pre[u][i] >= mod) pre[u][i] -= mod;
}
per(i,n,1){
suf[u][i] = suf[u][i+1] + f[u][i];
if(suf[u][i] >= mod) suf[u][i] -= mod;
}
return ;
}
#undef v
inline void work(){
init();read(n);
rg u,v;char ch;
rep(i,2,n){
u = i>>1;
while(ch=getchar(),ch<'!');
v = i;
if(ch == '<') add(u,v,-1),add(v,u,1);
else add(u,v,1),add(v,u,-1);
}
dfs(1,1);
int ans = 0;
rep(i,1,n){
ans += f[1][i];
if(ans >= mod) ans -= mod;
}
printf("%d\n",ans);
}
int main(){
int T;T = 1;
prework(100);
while(T--) work();
return 0;
}
bzoj 4822~4824 CQOI2017题解的更多相关文章
- BZOJ 1003 物流运输 题解 【SPFA+DP】
BZOJ 1003 物流运输 题解 Description 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的 ...
- BZOJ 1191 超级英雄 Hero 题解
BZOJ 1191 超级英雄 Hero 题解 Description 现在电视台有一种节目叫做超级英雄,大概的流程就是每位选手到台上回答主持人的几个问题,然后根据回答问题的多少获得不同数目的奖品或奖金 ...
- bzoj 4822: [Cqoi2017]老C的任务
4822: [Cqoi2017]老C的任务 练手速... #include <iostream> #include <cstdio> #include <cstring& ...
- bzoj 4824: [Cqoi2017]老C的键盘
Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 ...
- ●BZOJ 4822 [Cqoi2017]老C的任务
题链: https://www.luogu.org/problemnew/show/P3755 (洛谷上数据范围给全了的) 题解: 树状数组,离线询问 (本来想弄一个二维树状数组/二维RMQ,然后直接 ...
- BZOJ 4824 [Cqoi2017]老C的键盘 ——树形DP
每一个限制条件相当于一条有向边, 忽略边的方向,就成了一道裸的树形DP题 同BZOJ3167 唯一的区别就是这个$O(n^3)$能过 #include <map> #include < ...
- BZOJ 4822 [Cqoi2017]老C的任务 ——树状数组
直接离散化之后用树状数组扫一遍. 把每一个询问拆成四个就可以做了. %Silvernebula 怒写KD-Tree #include <map> #include <cmath> ...
- bzoj 4824: [Cqoi2017]老C的键盘【树形dp】
参考:https://www.cnblogs.com/FallDream/p/bzoj4824.html 画一画就会发现关系形成了一棵二叉树(其实看到n-1就能想到 然后dp,设f[i][j]为点i在 ...
- bzoj 4822: [Cqoi2017]老C的任务【扫描线+树状数组+二维差分】
一个树状数组能解决的问题分要用树套树--还写错了我别是个傻子吧? 这种题还是挺多的,大概就是把每个矩形询问差分拆成四个点前缀和相加的形式(x1-1,y1-1,1)(x2.y2,1)(x1-1,y2,- ...
随机推荐
- 【BZOJ2530】[Poi2011]Party (xia)构造
[BZOJ2530][Poi2011]Party Description 给定一张N(保证N是3的倍数)个节点M条边的图,并且保证该图存在一个大小至少为2N/3的团. 请输出该图的任意一个大小为N/3 ...
- 记录-移动端网页触摸内容滑动js插件
需求: 在webapp中需要左右滑动手机,移动主页的轮播图.也可用在引导页(欢迎页)的大图左右滑动 可用: 百度:swiper插件 在项目中导入插件,这里只有部分代码,具体百度swiper <l ...
- 详解Maven项目利用java service wrapper将Java程序生成Windows服务
在项目的开发中,有时候需要将Java应用程序打包成Windows服务,我们就直接可以通过windows的服务来启动和关闭java程序了. 本博文将通过有两种方法实现该功能,手动创建法和Maven自动打 ...
- OC中第三方库MJExtension的使用
MJExtension是一套常用的"字典和模型之间互相转换"的框架,在项目中也使用过,现在记录一下.随着Swift的普及,在Swift中也有一个类似功能的框架HandyJSON 也 ...
- 页游手游服务器(二)c支持mysql
上一篇说的是liua的net拓展,这一篇说lua的sql拓展,准确说是mysql拓展,这里推荐下postgre,比mysql好用,支持数组,各种好,不过腾讯平台不支持,所以你的公司要和腾讯合作,掂量下 ...
- 洛谷P2402 奶牛隐藏
洛谷P2402 奶牛隐藏 题目背景 这本是一个非常简单的问题,然而奶牛们由于下雨已经非常混乱,无法完成这一计算,于是这个任务就交给了你.(奶牛混乱的原因看题目描述) 题目描述 在一个农场里有n块田地. ...
- Django 之Form组件
Django之From组件 扩展:Django 之 ModelForm组件 Form组件功能 Django的Form主要具有一下几大功能 生成HTML标签 验证用户数据(显示错误信息) HTML Fo ...
- leetcode第一刷_Best Time to Buy and Sell Stock II
这道题尽管是上一道题的增强.可是反而简单了. 能够交易无数次,可是买卖必须成对的出现. 为了简单起见.我用abc三股股票来说明,且忽略掉相等的情况.三个数一共同拥有六种大小关系.注意他们之间的先后顺序 ...
- 变量动态选取资源ID
1.使用Resources 类的 getIdentifier方法 Resources res=getResources(); return res.getIdentifier(type ...
- php自定义的格式化时间示例代码
时间刚好是5分钟前,则对应的时间戳就会被格式化为5分钟前,自定义的格式化时间方法如下,感兴趣的朋友可以参考下 如:时间刚好是5分钟前,则对应的时间戳就会被格式化为5分钟前,不多说了,直接贴上代码: 复 ...