noip模拟30[毛毛毛探探探]
\(noip模拟30\;solutions\)
所以说,这次被初中的大神给爆了?????
其实真的不甘心,这次考场上的遗憾太多,浪费的时间过多,心情非常不好
用这篇题解来结束这场让人伤心的考试吧
\(T1\;毛一探\)
其实这个题我本来是考场上就能AC的
不得不说这个\(meet \;in\;the\;middle\)思想真的没谁了。
我在考场上一分钟想出来如何用一个复杂度不确定的办法来搞定他
(这个复杂度最劣是\(\mathcal{O(2^{n+1})}\),但是数据比较善良,给了我75pts,但是其实我这个方法就是正解,就是折半的思想)
想完之后我就觉得有点不太好,就开始优化我的暴力,\(1h20min\;later\),我醒悟了
发现自己根本优化不了,所以打了个我的算法,又打了一个更暴力的暴力,拍了拍没错。。。
我的暴力是根据两个人的分值的和相同来搞得,用了map和vector,存储能够达到每个可能的和的方案(也就是一个20位的二进制数)
每次找到一个和,就遍历相同的和的其他方案,如果没有重复的位,那么就标记,最后统计答案
75pts(暴力)
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define pa pair<int,int>
#define mpa(x,y) make_pair(x,y)
#define fi first
#define se second
const int N=25;
int n,m[N];
int dp[1<<20],ans,cnt;
bool vis[1<<20];
unordered_map<int,int> mp;
vector<int> vec[1<<20];
ll jc[N];
ll C(int x,int y){
return jc[x]/jc[y]/jc[x-y];
}
signed main(){
int a=scanf("%d",&n);
bool flag=0;
for(re i=1;i<=n;i++){
a=scanf("%d",&m[i]);
if(m[i]!=m[i-1]&&i!=1)flag=true;
}
/*if(!flag){
jc[0]=1;for(re i=1;i<=n;i++)jc[i]=jc[i-1]*i;
for(re i=2;i<=n;i+=2)ans+=C(n,i);
printf("%d",ans);
return 0;
}*/
dp[0]=0;
for(re i=1;i<=n;i++)dp[1<<(i-1)]=m[i];
for(re i=1;i<(1<<n);i++){
dp[i]=dp[i-(i&(-i))]+dp[i&(-i)];
if(mp.find(dp[i])==mp.end())
mp.insert(mpa(dp[i],++cnt));
int who=mp.find(dp[i])->se;
for(re j=0;j<vec[who].size();j++){
if(i&vec[who][j])continue;
vis[i|vec[who][j]]=true;
}
vec[who].push_back(i);
}
for(re i=1;i<(1<<n);i++)
if(vis[i])ans++;
printf("%d",ans);
return 0;
}
所以正解其实跟我的枚举方式差不多,不过是把原序列拆成两部分,
这一次我们无法枚举两个人分别的和,所以此时枚举状态的复杂度是\(\mathcal{O(3^{\frac{n}{2}})}\)
因为一道题可以不选,第一个人,第二个人。。
那我们统计答案的时候就需要利用两侧的选出来的两个人的元素之和的差值,
因为此时只要两测的两个人的差值相同,那么就会产生一个合法的答案
还是利用map和vector,只不过这次存的是两侧的差值,
还是最后直接统计答案,虽然我不会手写hash表,但是我跑的飞快
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define mpa(x,y) make_pair(x,y)
#define se second
#define fi first
const int N=25;
int n,n1,n2,w[N];
map<int,int> mp;
vector<int> vec[1<<20];
int cnt,ans;
bool vis[1<<20];
void dfs1(int now,int mx,int sum1,int sum2,int s){
if(now==mx+1){
int tmp=abs(sum1-sum2);
if(mp.find(tmp)==mp.end())
mp.insert(mpa(tmp,++cnt));
int who=mp.find(tmp)->se;
vec[who].push_back(s);
return ;
}
dfs1(now+1,mx,sum1+w[now],sum2,s|(1<<now-1));
dfs1(now+1,mx,sum1,sum2+w[now],s|(1<<now-1));
dfs1(now+1,mx,sum1,sum2,s);
}
void dfs2(int now,int mx,int sum1,int sum2,int s){
if(now==mx+1){
int tmp=abs(sum1-sum2);
if(mp.find(tmp)==mp.end())return ;
int who=mp.find(tmp)->se;
for(re i=0;i<vec[who].size();i++)
vis[s<<n1|vec[who][i]]=true;
return ;
}
dfs2(now+1,mx,sum1+w[now+n1],sum2,s|(1<<now-1));
dfs2(now+1,mx,sum1,sum2+w[now+n1],s|(1<<now-1));
dfs2(now+1,mx,sum1,sum2,s);
}
signed main(){
scanf("%d",&n);
n1=n/2;n2=n-n1;
for(re i=1;i<=n;i++)scanf("%d",&w[i]);
dfs1(1,n1,0,0,0);
dfs2(1,n2,0,0,0);
for(re i=1;i<(1<<n);i++)
if(vis[i])ans++;
printf("%d",ans);
}
说实话,这么多map的用法都是之前看一个std看到的
\(T2\;,毛二探\)
哈哈哈,这个题是我头一次想到dp题的一点点思路(考场上的时候)
然后考完试改这个题的时候只改了半个小时就过了,不得不说前缀和优化也是个好东西
就你先根据每个数的移动情况判断一下每个移动的优先级顺序
要是有一个顺序被赋值成两个不同的优先级,那么就直接输出零,因为一个排列不可能有两种优先级
如果知道了这些,那转移方程也就极其的好想了。
设\(f[i][j]\)表示我们目前处理q(就是那个优美序列,用来规划转移的)的第i个数,放在第j个位置
那么转移就是直接从上一层转移,如果当前交换需要比上一个早的话,那就插在上一个位置的前面,如果晚就放在后面。。。。这不用我说也知道吧
所以有了我们当前的\(\mathcal{O(n^3)}\)的转移,可以拿到70pts
O(n^3)暴力转移
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=5005;
const ll mod=1e9+7;
int n,w[N],mov[N];
int typ[N];
ll f[N][N],ans;
signed main(){
scanf("%d",&n);
for(re i=1;i<=n;i++){
scanf("%d",&w[i]);
w[i]++;
}
for(re i=1;i<=n;i++)
mov[w[i]]=i-w[i];
int flag=0;
for(re i=1;i<=n;i++){
if(mov[i]==0)flag=1;
if(mov[i]>0){
for(re j=i+1;j<i+mov[i];j++){
if(typ[j])flag=1;
typ[j]=2;
}
}
if(mov[i]<0){
for(re j=i-1;j>i+mov[i];j--){
if(typ[j])flag=1;
typ[j]=1;
}
}
}
if(flag){
printf("0");
return 0;
}
f[1][1]=1;
for(re i=2;i<n;i++){
for(re j=1;j<=i-1;j++){
if(typ[i]==1){
for(re k=1;k<=j;k++)
f[i][k]=(f[i][k]+f[i-1][j])%mod;
}
else{
for(re k=j+1;k<=i;k++)
f[i][k]=(f[i][k]+f[i-1][j])%mod;
}
}
}
for(re i=1;i<n;i++)ans=(ans+f[n-1][i])%mod;
printf("%lld",ans);
}
所以你打完这个暴力转移之后,你发现每一个状态都是从前一个的状态的一个连续区间转移来的
所以就可以直接前缀和优化,\(\mathcal{O(1)}\)转移了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=5005;
const ll mod=1e9+7;
int n,w[N],mov[N];
int typ[N];
ll f[N][N],ans,sum[N][N];
signed main(){
scanf("%d",&n);
for(re i=1;i<=n;i++){
scanf("%d",&w[i]);
w[i]++;
}
for(re i=1;i<=n;i++)
mov[w[i]]=i-w[i];
int flag=0;
for(re i=1;i<=n;i++){
if(mov[i]==0)flag=1;
if(mov[i]>0){
for(re j=i+1;j<i+mov[i];j++){
if(typ[j])flag=1;
typ[j]=2;
}
}
if(mov[i]<0){
for(re j=i-1;j>i+mov[i];j--){
if(typ[j])flag=1;
typ[j]=1;
}
}
}
if(flag){
printf("0");
return 0;
}
f[1][1]=1;
sum[1][1]=1;
for(re i=2;i<n;i++){
for(re j=1;j<=i-1;j++){
if(typ[i]==1){
f[i][j]=(f[i][j]+sum[i-1][i-1]+mod-sum[i-1][j-1])%mod;
}
else{
f[i][j+1]=(f[i][j+1]+sum[i-1][j]+mod)%mod;
}
}
for(re j=1;j<=i;j++){
sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
}
}
for(re i=1;i<n;i++)ans=(ans+f[n-1][i])%mod;
printf("%lld",ans);
}
\(T3\;毛三探\)
这个题就非常神奇的卡过了
直接枚举x,二分此时x能达到的最大重量的最小值,因为必须连续,判断直接\(\mathcal{O(n)}\)
如果只是这样那你就过不了了,
注意判断一下之前的x中的最有答案能不能合法,合法再去二分,不合法直接进行下一个x
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=10005;
int n,p,k,w[N],no[10005];
int ans=0x3f3f3f3f,sum;
bool check(int x){
int j=1;
for(re i=1;i<=k;i++){
int now=0;
for(;j<=n;j++){
if(now+no[j]>x)break;
now+=no[j];
}
if(j==n+1)return true;
}
return false;
}
signed main(){
scanf("%d%d%d",&n,&p,&k);
for(re i=1;i<=n;i++)scanf("%d",&w[i]),sum+=w[i];
for(re x=0;x<p;x++){
int l=0,r=sum,mid;
for(re i=1;i<=n;i++)no[i]=(w[i]+x)%p;
if(!check(ans))continue;
while(l<r){
mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
ans=min(ans,r);
}
printf("%d",ans);
}
noip模拟30[毛毛毛探探探]的更多相关文章
- NOIP 模拟 $30\; \rm 毛三琛$
题解 \(by\;zj\varphi\) 二分答案,考虑二分背包中的最大值是多少. 枚举 \(p\) 的值,在当前最优答案不优时,直接跳掉. 随机化一下 \(p\),这样复杂度会有保证. Code # ...
- NOIP 模拟 $30\; \rm 毛二琛$
题解 \(by\;zj\varphi\) 原题问的就是对于一个序列,其中有的数之间有大小关系限制,问有多少种方案. 设 \(dp_{i,j}\) 表示在前 \(i\) 个数中,第 \(i\) 个的排名 ...
- NOIP 模拟 $30\; \rm 毛一琛$
题解 \(by\;zj\varphi\) 如何判断一个集合可以被拆成两个相等的部分? 枚举两个集合,如果它们的和相等,那么他们的并集就是合法的,复杂度 \(\mathcal O\rm(3^n)\) \ ...
- Noip模拟30 2021.8.4
T1 毛一琛 考场上打的稳定的$O((2^n)^2)$的暴力.其实再回忆一下上次那道用二进制枚举的题$y$ 就可以知道一样的道理,使用$\textit{Meet In the Middle}$, 按照 ...
- 2021.8.4考试总结[NOIP模拟30]
T1 毛衣衬 将合法子集分为两个和相等的集合. 暴力枚举每个元素是否被选,放在哪种集合,复杂度$O(3^n)$.考虑$\textit{meet in the middle}$. 将全集等分分为两部分分 ...
- noip模拟30
\(\color{white}{\mathbb{缀以无尽之群星点点,饰以常青之巨木郁郁,可细斟木纹叶脉,独无可极苍穹之览,名之以:密林}}\) 看完题后感觉整套题都没什么思路,而且基本上整场考试确实是 ...
- NOIP模拟 30
补坑,很多都忘了. T1 树 像我这种人都能考场A掉当然是道水题辣 求出每条有向边的期望就好了 T2 回文串 当时毫无思路,暴力写挂. 首先把B转过来,那么都变成后缀的前缀拼起来 对于每一个LCP,他 ...
- 「题解」NOIP模拟测试题解乱写I(29-31)
NOIP模拟29(B) T1爬山 简单题,赛时找到了$O(1)$查询的规律于是切了. 从倍增LCA那里借鉴了一点东西:先将a.b抬到同一高度,然后再一起往上爬.所用的步数$×2$就是了. 抬升到同一高 ...
- noip模拟33
\(\color{white}{\mathbb{失足而坠千里,翻覆而没百足,名之以:深渊}}\) 这场考试的时间分配非常不科学 开题试图想 \(t1\) 正解,一个半小时后还是只有暴力,特别惊慌失措 ...
随机推荐
- javascript之一切都是对象
在学习的过程中,我们常常能听到这样一句话:一切皆是对象.那么这句话该如何理解呢?首先,我们要明确对象的概念.要明白除了基本数据类型都是对象. typeof操作符是大家经常使用的,我们常用它来检测给定变 ...
- 33、awk命令详解
33.1.命令介绍: awk不仅仅是linux系统中的一个命令,而且是一种编程语言,可以用来处理数据和生成报告. awk的数据可以是一个或多个文件,可以是来自标准输入,也可以通过管道获取标准输入,aw ...
- 7.7、深入解析openstak工作流程
1.openstack的使用: (1)使用openstack创建的用户默认是default域,角色是user; (2)域-->角色-->用户-->项目 (3)配额在管理员登录后再项目 ...
- BGV方案
BGV方案 SIMD技术 中国剩余定理 在<孙子算经>中有这样一个问题:"今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物 ...
- POJ 1016 Numbers That Count 不难,但要注意细节
题意是将一串数字转换成另一种形式.比如5553141转换成2个1,1个3,1个4,3个5,即21131435.1000000000000转换成12011.数字的个数是可能超过9个的.n个m,m是从小到 ...
- 【Azure 应用服务】Azure Function App 执行PowerShell指令[Get-Azsubscription -TenantId $tenantID -DefaultProfile $cxt]错误
问题描述 使用PowerShell脚本执行获取Azure订阅列表的指令(Get-Azsubscription -TenantId $tenantID -DefaultProfile $cxt).在本地 ...
- 超详细的原生JavaScript轮播图(幻灯片)的制作
本次轮播图的制作主要分为3个部分,分别是:设置定时器自动轮播:点击左右切换按钮轮播:下方点击按钮轮播.具体实现步骤如下: (效果图) html部分代码如下: <div class="s ...
- mac sudo: /etc/sudoers is world writable
今天误操作修改了/etc/sudoers的权限,将它的权限改成了777,结果就导致执行所有sudo的命令都报错. sudo: /etc/sudoers is world writable sudo: ...
- Java | this的本质 和 static的本质
this 在说this之前先说一下,对象创建的过程: 1.分配对象空间,并将对象成员变量初始化. 2.执行属性值的显式初始化. 3.执行构造方法. 4.返回相关的地址给相关的对象. this的本质 ...
- vmware使用U盘安装系统
创建好系统 创建一个新的硬盘,选择"physicalDrive1" 如果识别不到physicalDrive 1,使用下面的方法. 1.在本机的服务里面启用下面的服务. 2.重启 V ...