2021.5.23 noip模拟2(排序|划艇|放棋子)
今天比昨天更惨,惨炸了
看到T1不会,跳!!!
T2不会,再跳!!!!
T3不会,后面没题了;;;;
无奈无奈,重新看T1,然鹅时间已经过去了一个小时
然而我一想不出题来就抱着水瓶子喝水,然后跑厕所,然后思路一直断。。。。
但是吧看到题解之后一切就恍然大悟了
哈哈哈哈没事慢慢锻炼,以后的考试一定得上100吧
看到这分数,怎么样,震惊到了吧,倒数第三那个是我,这都是大佬,别看我还有15分,蒙出来的,一点技术含量都没有
T1排序
N<=12;;;;
可以看到这数据属实很小啊
其实考场上我想到了正解,可惜我没敢打,(凭什么这题正解是dfs)那可是深搜啊!!!(暴力象征啊,怎么能是他呢?!?!?!?)
说正解吧;;
可以得到:对于所有操作来说,操作顺序对结果没有任何影响
(哼考场上我发现这个规律了,所以打了个阶乘就走了,没想到还有15pts)
所以我们可以从最小的i开始dfs
首先我们要判断这样的块中有多少不符合规律的(注意当前如果是2x那么判断的时候要判断长度为2x-1的区间(这很显然吧))
有四种情况:
1、有2个以上的不符合规律的区间。那就没法交换啦,只让交换一次,所以可以直接返回了,00000
2、没有不符合规律的。那就直接去下一层x+1
3、有1个不符合规律的区间。那就交换它前后的区间就行了
4、有2个不符合规律的区间。那么这时候可以两个不符合的互相交换,没准就成了呢(注意交换完了之后要判断,然后再换回去)判断成功后才能进入下一层
再定义一个计数器sum,记录一共交换了多少次
还有dfs的时候别忘了sum++,然后用完之后在sum--;
判断是否符合规律的时候有两种情况:1、前面的比后面的大
2、前后的差值大于1(这个我是考后才发现的,原来这个区间里的数是连续的,一开始我还想离散化嘞!!!)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1<<13;
int n,m,nn;
int a[N];
int jc[15],po[15];
int ans,sum;
bool flag(int l,int r){
if(l==r)return true;
for(re i=l+1;i<=r;i++)
if(a[i]<a[i-1]||a[i]-a[i-1]>1)return false;
return true;
}
void swp(int l,int r,int len){
int b[N];
for(re i=0;i<len;i++)b[i]=a[l+i];
for(re i=0;i<len;i++)a[l+i]=a[r+i];
for(re i=0;i<len;i++)a[r+i]=b[i];
}
void dfs(int x){
int num=0;
int p[100];
for(re i=1;i<=po[n];i+=po[x])
if(!flag(i,i+po[x]-1))
p[++num]=i,p[++num]=i+po[x-1];
if(num>4)return ;
if(num==0)
if(x==n)ans+=jc[sum];
else dfs(x+1);
else if(num==2){
sum++;
swp(p[1],p[2],po[x-1]);
if(flag(p[1],p[1]+po[x]-1))
if(x==n)ans+=jc[sum];
else dfs(x+1);
swp(p[1],p[2],po[x-1]);
sum--;
}
else if(num==4){
sum++;
for(re i=1;i<=2;i++){
for(re j=3;j<=4;j++){
swp(p[i],p[j],po[x-1]);
if(flag(p[1],p[1]+po[x]-1)&&flag(p[3],p[3]+po[x]-1))
if(x==n)ans+=jc[sum];
else dfs(x+1);
swp(p[i],p[j],po[x-1]);
}
}
sum--;
}
}
int main(){
scanf("%d",&n);
po[0]=1;jc[0]=1;
for(re i=1;i<=n;i++)jc[i]=jc[i-1]*i,po[i]=po[i-1]*2;
for(re i=1;i<=po[n];i++){
scanf("%d",&a[i]);
}
dfs(1);
printf("%d",ans);
}
Code
T2划艇
这是这场考试中最难的一个题了
考试的时候完全没有思路,想要通过dfs骗来9分没想到爆零了 ,害害害害害
首先想到的一定是dp;
(不对不对不对,首先想到的应该是离散化,毕竟数据范围太大了。。。QAQ)
设f[i][j]表示第i个学校派出j条船所能达到的方案数
啊呀,你突然就发现,这每个学校的a[i]和b[i]达到的区间会有重复的部分啊(完了完了完了转移不了了,然后你就放弃了dp,成功的躲避了正解)
凡事都得试试看啊,不试试就放弃,是不是傻啊
突然你就发现好像可以转移诶
这时候涉及到离散化的时候的一个小细节------------左闭右开(其实左开右闭也可以啦,都是为了让整个区间不重不漏,就看个人习惯了)
将a[i]和b[i]+1塞到离散化数组里,在设计转移方程的时候,注意循环的范围是(<)
转移的时候要分两种情况:
1、i-1这个学校派出的划艇不在a[i]~b[i]-1中,那么就可以直接转移,此时的i学校可以有方案数就是C(b[i]-a[i],1),那么再乘上前面所有的方案数就好了
(那么问题来了,怎么得到前面所有的方案数呢。。
设sum[i][j]表示f[i][j]的二维前缀和,怎么求呢:sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+f[i][j];
可能这也叫容斥原理吧,把多加上的给剪掉就好了)。
2、i-1和它以前的学校有一部分派出了在这个区间之内的划艇,那么就要再设一个未知数k,它表示从第k个开始,后面的学校派出了a[i]~b[i]-1只划艇
这个转移比较难设计,f[i][j]+=组合数*sum[k-1][j-1];这个组合数怎么求呢
(再引入一个数组,len[ ],表示离散化数组里lsh[i]~lsh[i+1]的长度,(向后lsh[i+1]的长度,而不是向lsh[i-1]的,不然转移方程要修改的)
首先我们要在了len[i]这么长的区间内寻找k个数(就像在一个递增区间内找k个数递增一样)有C(len[i],k)这么多种选择,当然这其中会有学校不派出划艇
所以在i-k-1个学校中选出p-2学校来(不包括i和k)C(i-k+1,p-2)这个要乘好多p从1~i-k+1所以要手动搞一下,把它展开在合并,最后变成一个可以边转移边求出来的组合数,乘上就好了
还要注意,sum[i][j],不仅要吧a[i]~b[i]-1的f[i][j]加上,别的地方也要加上
然后我们就可以开开心心的把代码粘上了!!!!
记得开long long哟,不开long long见祖宗哦
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register int
const int mod=1e9+7;
const int N=505;
int n;
int l[N],r[N],len[N*2];
int lsh[N*2],lh,ls[N*2];
int inv[N];
int dp[N][N*2],sum[N][N*2];
map<int,int> p;
signed main(){
scanf("%lld",&n);
inv[1]=inv[0]=1;
for(re i=2;i<=500;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(re i=1;i<=n;i++){
scanf("%lld%lld",&l[i],&r[i]);
lsh[i*2-1]=l[i];
lsh[i*2]=r[i]+1;
}
sort(lsh+1,lsh+2*n+1);
for(re i=1;i<=2*n;i++){
if(lsh[i]!=lsh[i-1]){
ls[++lh]=lsh[i];
p[lsh[i]]=lh;
}
}
for(re i=1;i<=n;i++){
l[i]=p[l[i]];
r[i]=p[r[i]+1];
}
sum[0][0]=dp[0][0]=1;
ls[lh+1]=ls[lh]+1;
for(re i=1;i<=lh;i++)len[i]=ls[i+1]-ls[i];
for(re i=1;i<=n;i++)sum[i][0]=1;
for(re i=1;i<=lh;i++)sum[0][i]=1;
for(re i=1;i<=n;i++){
for(re j=1;j<l[i];j++)
sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[i][j]+mod)%mod;
for(re j=l[i];j<r[i];j++){
dp[i][j]=sum[i-1][j-1]*len[j]%mod;
int c=len[j]-1;
int can=1;
for(re k=i-1;k>=1;k--){
if(l[k]<=j&&j<r[k]){
can++;
c=c*((can+len[j]-2)*inv[can]%mod)%mod;
dp[i][j]=(dp[i][j]+sum[k-1][j-1]*c%mod)%mod;
}
}
sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[i][j]+mod)%mod;
}
for(re j=r[i];j<=lh;j++)
sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[i][j]+mod)%mod;
}
printf("%lld",sum[n][lh]-1);
}
Code
T3放棋子
感谢赵队一句话点懂了我
二维转移不了就用三维的啊
那么我瞬间感觉自己又行了
设f[i][j][k]为占据i行j列并且放了前k种颜色的棋子的方案数,然后你发现这根本不能直接求出来!!!
所以再设,g[i][j][k]为占据i行j列(这里指的是占满,就是前i行和前j列中哪个格子都不能再放其他种类的棋子了)并且放了k个相同种类的棋子
可以看到f[i][j][k]可以由f[1~i][1~j][ ]转移过来,那么这个式子我打不出来,所以你们自己看代码,总共五层循环!!!
f[i][j][k]转移的时候要乘组合数,就是空出哪几行和哪几列的方案数,最后要乘上g[i][j][col[k]](col[k]就是输入的第k种棋子的个数)
那么g[i][j][k]咋求嘞,你又有重大发现,这个不能直接求 ------废话!!
还是容斥原理一下呗:g[i][j][k]=C(i*j,k)-g[a][b][k]*C(i,i-a)*C(j,j-b)就是减去那些有空行空列的情况
然后就是代码时间
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int mod=1e9+9;
int n,m,e;
int col[15];
ll f[35][35][15];
ll g[35][35][1005];
ll c[1005][1005];
ll ans;
signed main(){
scanf("%d%d%d",&n,&m,&e);
for(re i=1;i<=e;i++)
scanf("%d",&col[i]);
c[0][0]=1;
for(re i=1;i<=1000;i++){
c[i][0]=c[i][i]=1;
for(re j=1;j<i;j++){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
for(re i=1;i<=n;i++)
for(re j=1;j<=m;j++)
for(re k=max(i,j);k<=i*j;k++){
g[i][j][k]=c[i*j][k];
for(re a=1;a<=i;a++)
for(re b=1;b<=j;b++){
if(a*b<k||(a==i&&b==j))continue;
g[i][j][k]=(g[i][j][k]-g[a][b][k]*c[i][a]%mod*c[j][b]%mod+mod)%mod;
}
}
f[0][0][0]=1;
for(re i=1;i<=n;i++)
for(re j=1;j<=m;j++){
for(re k=1;k<=e;k++){
if(i*j<col[k])continue;
for(re a=0;a<=i;a++)
for(re b=0;b<=j;b++)
if((i-a)*(j-b)>=col[k])
f[i][j][k]=(f[i][j][k]+f[a][b][k-1]*c[n-a][i-a]%mod*c[m-b][j-b]%mod*g[i-a][j-b][col[k]]%mod)%mod;
}
ans=(ans+f[i][j][e])%mod;
}
printf("%lld",ans);
}
完结
∑l=1i−1∑r=1j−1f[l][r]+∑l=1k−1∑r=1j−1f[l][
2021.5.23 noip模拟2(排序|划艇|放棋子)的更多相关文章
- 2021.5.22 noip模拟1
这场考试考得很烂 连暴力都没打好 只拿了25分,,,,,,,,好好总结 T1序列 A. 序列 题目描述 HZ每周一都要举行升旗仪式,国旗班会站成一整列整齐的向前行进. 郭神作为摄像师想要选取其中一段照 ...
- 9.23 noip模拟试题
Problem 1 抓牛(catchcow.cpp/c/pas) [题目描述] 农夫约翰被通知,他的一只奶牛逃逸了!所以他决定,马上出发,尽快把那只奶牛抓回来. 他们都站在数轴上.约翰在N(O≤N ...
- 10.23 noip模拟试题
尼玛蛋pdf好难粘 直接写了 T1 /*开始写wa了 我真弱2333 关于p的排序规则不只是差值 为了字典序最小 还要拍别的*/ #include<cstdio> #include< ...
- 2018.10.23 NOIP模拟 “新”的家园(缩图+dijksta/spfa)
传送门 考试70分骗分写挂了=30分=全场最低. 哎今天230垫底了. 这题出的挺好. 对于非关键点直接缩点. 每次把要查的insertinsertinsert进缩好的图里面跑spfa/dijkstr ...
- 2018.10.23 NOIP模拟 行星通道计划(bit)
传送门 卡常题. 成功卡掉了作死写树套树的zxy. 然而对我的二维bit无能为力. 直接维护两棵bit. bit1[i][j]bit1[i][j]bit1[i][j]表示左端点小于等于iii,右端点小 ...
- 2018.10.23 NOIP模拟 战争(并查集)
传送门 跟bzoj1015: [JSOI2008]星球大战是同一道题啊讲道理. 随便合并一下就能过了. 代码
- 9.23 NOIP模拟题(数学专练)
数论基础 专题测试 命题人:清华大学 王赢绪 /* 水题 答案为C(n-k,m-1) 预处理阶乘和逆元,O(1)算答案 开始读错题了!!!朱一乐!!! */ #include<iostream ...
- noip模拟赛 排序
分析:因为序列是不严格单调的,所以挪动一个数其实就相当于把这个数给删了.如果a[i] < a[i-1],那么可以删掉a[i],也可以删掉a[i-1](!如果没考虑到这一点就只有90分),删后判断 ...
- 【noip模拟赛8】魔术棋子
描述 在一个M*N的魔术棋盘中,每个格子中均有一个整数,当棋子走进这个格子中,则此棋子上的数会被乘以此格子中的数.一个棋子从左上角走到右下角,只能向右或向下行动,请问此棋子走到右下角后,模(mod)K ...
随机推荐
- shackdow-socks 搭建
搭建步骤 wget --no-check-certificate https://raw.githubusercontent.com/teddysun/shadow-1-socks_install/m ...
- 关于Snowflake 生成53位ID
1, bug现象: 没有经过处理的Snowflake 生成的是64位bit的唯一ID,但由于多数时候我们前台用到js,但是js只支持53位bit的数值.这样就导致了传到前台的64位的丢失精度. 解决思 ...
- hdu1245 两个权值的最短路
题意: 求s到t的最短路,如果路径相同求那么要求另一个权值尽可能的小. 思路: 水题,就是spfa的比较那个地方多了一个可以更新的机会,当(s_x[xin] > s_x[ ...
- 洛谷P1553 数字反转(升级版)
题目简介 题目描述 给定一个数,请将该数各个位上数字反转得到一个新数. 这次与NOIp2011普及组第一题不同的是:这个数可以是小数,分数,百分数,整数.整数反转是将所有数位对 ...
- Win64 驱动内核编程-10.突破WIN7的PatchGuard
突破WIN7的PatchGuard WIN64 有两个内核保护机制,KPP 和 DSE.KPP 阻止我们 PATCH 内核,DSE 拦截我们加载驱动.当然 KPP 和 DSE 并不是不可战胜的,WIN ...
- TLS实现代码段加密
刚开始见到这个思路是看到周大师用这个东西做的免杀,当时感觉这个想法很好,但是由于当时对PE结构了解的少,看到二进制的东西就打怵,所以当时也没能成功的去实现这个思路,只是简单的记录了一下TLS的特性,直 ...
- Java安全之FastJson JdbcRowSetImpl 链分析
Java安全之FastJson JdbcRowSetImpl 链分析 0x00 前言 续上文的Fastjson TemplatesImpl链分析,接着来学习JdbcRowSetImpl 利用链,Jdb ...
- 【BUAA软工】Beta阶段事后分析
设想与目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 解决的问题 总体解决的问题:新手编程者配置编程环境难.本地编写的代码跨设备同步难.本地ide安装使用过程 ...
- 数据流分析软件SQLFlow的高阶模式Job任务介绍
SQLFlow是一个可视化的在线处理SQL对象依赖关系的工具,只需要上传你的SQL脚本,它可以自动分析SQL里的数据对象,包括database.schema.table.view.column.pro ...
- java基础——if和Swith的应用
顺序结构 java的基本结构就是顺序结构,除非特别说明,否则就按照一句一句的执行 它是任何一个算法都离不开的基本算法结构 选择结构 if单选择 if双选择 if多选择 嵌套的if结构 switch多选 ...