T1:

把每一行状压,按行DP。设fi,j,k,i表示第几行,j是当前行的1覆盖状态,k是当前行选择按钮的状态。转移的时候枚举j和k,再枚举下一层的按钮选择情况l。如果l和j可以全覆盖当前层则转移合法,根据下一层选择l状态的代价进行转移。预处理一行每一种选法i可以覆盖到的状态di,各行选择按钮状态i对应的代价dpi,以及每一行的初始状态bi。转移时下一层的覆盖情况就是k|dl|bi+1。初始化第一层是所有选法i对应的代价,即f1,d[i]|b[1],i=dp1,i

整个DP过程的复杂度是O(3m*2m*n)。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=,maxn=<<N;
int n,maxx,ans,m,flag;
int s[N][N],a[N][N],dp[N][maxn],f[N][maxn][maxn],b[N],d[maxn];
int main()
{
scanf("%d%d",&n,&m);
maxx=<<m;
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%1d",&s[i][j]);
b[i]|=(s[i][j]<<(j-));
}
}
for(int i=;i<maxx;i++){
d[i]=(i|(i<<)|(i>>))&(maxx-);
}
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%d",&a[i][j]);
}
for(int j=;j<maxx;j++){
for(int k=;k<=m;k++){
if(j&(<<(k-))){
dp[i][j]+=a[i][k];
}
}
}
}
memset(f,0x3f3f3f3f,sizeof(f));
for(int j=;j<maxx;j++){
f[][d[j]|b[]][j]=min(f[][d[j]|b[]][j],dp[][j]);
}
for(int i=;i<n;i++){
for(int j=;j<maxx;j++){
if((j&b[i])!=b[i])continue;
for(int k=j;k>=;k=(k-)&j){
if((d[k]&j)!=d[k]&&k)continue;
for(int l=;l<maxx;l++){
if((j|l)==(maxx-)){
f[i+][d[l]|k|b[i+]][l]=min(f[i+][d[l]|k|b[i+]][l],f[i][j][k]+dp[i+][l]);
}
}
if(!k)break;
}
}
}
ans=0x3f3f3f3f;
for(int i=;i<maxx;i++)ans=min(ans,f[n][maxx-][i]);
printf("%d\n",ans);
return ;
}

处理b的时候反着处理了整张图,并且初始化第一行的时候出了锅没考虑b数组,于是写了一下午。

T2:

第一问比较容易。把求方案数的过程看成一个往序列里添加元素的过程。将原序列按第一关键字val从大到小,第二关键字key从小到大排序。val从大到小保证了后添加的元素无论放在哪都不会影响前面放进来的元素,并且已经存在的每一个元素都会对当前元素的key值产生贡献。所以只用考虑当前要放的元素本身的限制,考虑怎么求这一次操作的方案数,即新加入的元素能放在哪些地方。如果序列里的元素个数小于keyi,那么显然最多只有i个位置可以放(1->i)。如果序列里的元素多于等于keyi个,那么也只有前keyi个位置可以放,这样才不会超出key的限制。特别的状况是存在相同的val,这就是前面key作为第二关键字从小到大排序的原因。相同val的key从小到大,保证对于后加入的元素,先加入的val相同的元素所在的位置也可以放当前的元素。先加入的元素为后加入的元素创造了新的位置,它所在的地方后加入的元素一定可以放到。于是对于相同的val累计一个num,num从已有的第二个相同val开始累计,ans=(ans*min(i,keyi+num))%mod。

第二问愣是没想出来,也没看明白题解里平衡树的做法。好像是根据第一问的过程,每次插入一个元素的时候,贪心地放在能放的最大位置之前的第一个字典序比它大的元素前面。然后就不会写了。

题解的第二种做法是利用线段树,也是贪心地构造一个序列。一开始所有元素都是未加入元素。如果已经加入的元素的个数小于手中所有元素的key值-1,那么随便把哪个元素扔进序列都合法。根据题目要求,显然选择未加入的元素中字典序最小的。如果未加入的元素中存在key被卡在界限上,那么要么把它丢进序列,要么在val小于等于它的元素中选择更优的丢进序列。于是可以想到把原序列按val第一关键字从小到大key第二关键字从小到大排序,用线段树维护序列。选择要加入序列的元素就是在线段树的一段区间上选择key最小的元素。每次将一个元素扔进序列后,把线段树上这个元素的所有信息都变成inf,并且把所有未加入序列且val比它小的元素的key值-1,即线段树上的区间减。这里的key其实要记两次,一个记作用来当作限制,进行区间减操作。一个始终不变,用来在区间查询的时候作为可能答案进行比较。更新区间最小限制值以及key值的时候若出现值相同的元素,更新为在线段树上更靠前的,也就是val更小的。一个是构造的序列要求第二关键字val也是从小到大。另一个是若有多个key值同时等于1,要用val最小的来限制答案。否则根据更大的val选择出来的答案可能不满足val小的key的限制。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,mod=1e9+,num;
const int N=*1e5+,inf=;
long long ans;
struct node{
int key,val;
}a[N];
struct tree{
int l,r,dic,dici,val,vali,tag,top,topi;
}b[N*];
bool cmp(node a,node b){
if(a.val>b.val)return true;
else if(a.val==b.val){
if(a.key<b.key)return true;
else return false;
}
else return false;
}
bool cmp1(node a,node b){
if(a.val<b.val)return true;
else if(a.val==b.val){
if(a.key<b.key)return true;
else return false;
}
else return false;
}
void pushup(int p){
if(b[p*].dic<b[p*+].dic){
b[p].dic=b[p*].dic;
b[p].dici=b[p*].dici;
}
else if(b[p*].dic==b[p*+].dic){
b[p].dic=b[p*].dic;
if(b[p*].dici<b[p*+].dici){
b[p].dici=b[p*].dici;
}
else b[p].dici=b[p*+].dici;
}
else{
b[p].dic=b[p*+].dic;
b[p].dici=b[p*+].dici;
}
if(b[p*].val<b[p*+].val){
b[p].val=b[p*].val;
b[p].vali=b[p*].vali;
}
else if(b[p*].val==b[p*+].val){
b[p].val=b[p*].val;
if(a[b[p*].vali].key<a[b[p*+].vali].key){
b[p].vali=b[p*].vali;
}
else{
b[p].vali=b[p*+].vali;
}
}
else{
b[p].val=b[p*+].val;
b[p].vali=b[p*+].vali;
}
if(b[p*].top<b[p*+].top){
b[p].top=b[p*].top;
b[p].topi=b[p*].topi;
}
else if(b[p*].top==b[p*+].top){
b[p].top=b[p*].top;
if(b[p*].topi<b[p*+].topi){
b[p].topi=b[p*].topi;
}
else b[p].topi=b[p*+].topi;
}
else{
b[p].top=b[p*+].top;
b[p].topi=b[p*+].topi;
}
}
void pushdown(int p){
if(b[p].tag){
b[p*].tag+=b[p].tag;
b[p*].dic-=b[p].tag;
b[p*+].tag+=b[p].tag;
b[p*+].dic-=b[p].tag;
b[p].tag=;
}
}
void build(int p,int l,int r){
b[p].l=l,b[p].r=r;
if(l==r){
b[p].dic=a[l].key,b[p].dici=l;
b[p].val=a[l].val,b[p].vali=l;
b[p].top=a[l].key,b[p].topi=l;
return;
}
int mid=(l+r)/;
build(p*,l,mid);
build(p*+,mid+,r);
pushup(p);
}
void change(int p,int l,int r,int y){
if(l<=b[p].l&&b[p].r<=r){
b[p].dic=b[p].val=b[p].top=y;
a[b[p].l].key=y;
return;
}
pushdown(p);
int mid=(b[p].l+b[p].r)/;
if(l<=mid)change(p*,l,r,y);
if(r>mid)change(p*+,l,r,y);
pushup(p);
}
void change1(int p,int l,int r,int y){
if(l<=b[p].l&&b[p].r<=r){
b[p].dic--;
b[p].tag++;
return;
}
pushdown(p);
int mid=(b[p].l+b[p].r)/;
if(l<=mid)change1(p*,l,r,y);
if(r>mid)change1(p*+,l,r,y);
pushup(p);
}
int ask(int p,int l,int r){
if(l<=b[p].l&&b[p].r<=r){
return b[p].topi;
}
pushdown(p);
int mid=(b[p].l+b[p].r)/;
int x=;
a[x].key=a[x].val=inf;
if(l<=mid){
int y=ask(p*,l,r);
if(a[y].key<a[x].key)x=y;
else if(a[y].key==a[x].key){
if(a[y].val<a[x].val)x=y;
}
}
if(r>mid){
int y=ask(p*+,l,r);
if(a[y].key<a[x].key)x=y;
else if(a[y].key==a[x].key){
if(a[y].val<a[x].val)x=y;
}
}
pushup(p);
return x;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d%d",&a[i].key,&a[i].val);
}
sort(a+,a+n+,cmp);
ans=;
for(int i=;i<=n;i++){
if(i>&&a[i].val==a[i-].val)num++;
else num=;
ans=ans*min(i,a[i].key+num)%mod;
}
printf("%lld\n",ans);
sort(a+,a+n+,cmp1);
build(,,n);
for(int i=,x;i<=n;i++){
if(b[].dic==)x=ask(,,b[].dici);
else x=b[].topi;
printf("%d %d\n",a[x].key,a[x].val);
if(x>)change1(,,x-,-);
change(,x,x,inf);
}
return ;
}

第一问没想多长时间,第二问也没改多长时间(至少比我作为全场倒数改出来的T1要强),但是考场上就是没想出第二问怎么写。按理说贪心的思路也不是很难想…

T3:

不会,没写,咕着。

关于像在长郡考试那次一样把自己的电脑卡死机两次还不得不关机一次这件事情我还能说什么呢我给自己找扇窗户吧

关于T1不好好打暴力搜索非要搞玄学状压搜索这件事情我都不用给自己找扇窗户了就地蒸发吧

菜 墨雨笙 菜

2019.10.21 csp-s模拟测试81 反思总结的更多相关文章

  1. 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组

    2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...

  2. 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)

    2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...

  3. 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色

    2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...

  4. 2019.10.29 CSP%您赛第四场t2

    我太菜了我竟然不会分层图最短路 ____________________________________________________________________________________ ...

  5. 2019.8.9 NOIP模拟测试15 反思总结

    日常爆炸,考得一次比一次差XD 可能还是被身体拖慢了学习的进度吧,虽然按理来说没有影响.大家听的我也听过,大家学的我也没有缺勤多少次. 那么果然还是能力问题吗……? 虽然不愿意承认,但显然就是这样.对 ...

  6. 2019.8.1 NOIP模拟测试11 反思总结

    延迟了一天来补一个反思总结 急匆匆赶回来考试,我们这边大家的状态都稍微有一点差,不过最后的成绩总体来看好像还不错XD 其实这次拿分的大都是暴力[?],除了某些专注于某道题的人以及远程爆踩我们的某学车神 ...

  7. 2019/10/17 CSP模拟 总结

    T1 补票 Ticket 没什么好说的,不讲了 T2 删数字 Number 很后悔的是其实考场上不仅想出了正解的方程,甚至连优化都想到了,却因为码力不足只打了\(O(n^2)\)暴力,甚至还因为细节挂 ...

  8. 2019.10.30 csp-s模拟测试94 反思总结

    头一次做图巨的模拟题OWO 自从上一次听图巨讲课然后骗了小礼物以后一直对图巨印象挺好的233 T1: 对于XY取对数=Y*log(x) 对于Y!取对数=log(1*2*3*...*Y)=log1+lo ...

  9. 2019.8.10 NOIP模拟测试16 反思总结【基本更新完毕忽视咕咕咕】

    一如既往先放代码,我还没开始改… 改完T1滚过来了,先把T1T2的题解写了[颓博客啊] 今天下午就要走了,没想到还有送行的饯别礼,真是欣喜万分[并没有] 早上刚码完前面的总结,带着不怎么有希望的心情开 ...

随机推荐

  1. 基于MFC的实时可视化项目中视图刷新消息的严谨使用

    在实时可视项目中,视图的实时刷新显示对软件的体验感来说非常重要,当算法的效率达到实时,比如一秒40帧,如果实时显示帧率更不上,则体验感将大大折扣,让用户感觉你的算法并没有40帧,当然最关键的是解决显示 ...

  2. memcached 技术支持

    1. Install sudo apt-get install memcached 2.启动和停止 启动: service memcached start 停止: service memcached ...

  3. day 48 jQuery快速入门

    jQuery快速入门   jQuery jQuery介绍 1.jQuery是一个轻量级的.兼容多浏览器的JavaScript库. 2.jQuery使用户能够更方便地处理HTML Document.Ev ...

  4. PAT甲级——A1105 Spiral Matrix【25】

    This time your job is to fill a sequence of N positive integers into a spiral matrix in non-increasi ...

  5. Python运算符,逻辑运算

    运算符 计算机可以进行的运算有很多种,可不只加减乘除这么简单,运算按种类可分为算数运算.比较运算.逻辑运算.赋值运算.成员运算.身份运算.位运算,今天我们暂只学习算数运算.比较运算.逻辑运算.赋值运算 ...

  6. ckeditor图片上传二三事

    最近实验室要用ckeditor,踩了几个小坑记录下. 1.出现iframe跨域问题 response.setHeader("X-Frame-Options", "SAME ...

  7. net.sf.json JSONObject与JSONArray总结

    JSONObject:json对象,就是一个键对应一个值,使用的是大括号{ },如:{key:value} JSONArray:json数组,使用中括号[ ],只不过数组里面的项也是json键值对格式 ...

  8. [NOI2015] 软件包管理器【树链剖分+线段树区间覆盖】

    Online Judge:Luogu-P2146 Label:树链剖分,线段树区间覆盖 题目大意 \(n\)个软件包(编号0~n-1),他们之间的依赖关系用一棵含\(n-1\)条边的树来描述.一共两种 ...

  9. CA证书制作

    目录 手动制作CA证书 1.安装 CFSSL 2.初始化cfssl 3.创建用来生成 CA 文件的 JSON 配置文件 4.创建用来生成 CA 证书签名请求(CSR)的 JSON 配置文件 5.生成C ...

  10. linux学习(三)-----linux用户管理、实用指令

    用户管理 基本介绍 说明: 1.Linux 系统是一个多用户多任务的操作系统,任何一个要使用系统资源的用户,都必须首先向 系统管理员申请一个账号,然后以这个账号的身份进入系统. 2.Linux 的用户 ...