被打回原形了emmmmm

贴张图吧,展示一下根本不行的水平

考试经过

上来浏览一遍T1到T3,读懂题之后发现都不是很可做

T1上了想到了前缀和,往矩阵快速幂想了一下觉得不可做,半小时之后还是只会\(n_4\)的暴力,然后调了半天暴力。。。

事实上这个题在我的洛谷推荐上,我还点进去过,可惜没仔细看……

T2想了一下先想的是贪心,觉得不太对就没打,后来发现树形dp有一万分,果断开打,\(k=1\)调了大概一个小时,由于不认为自己能打对\(k=2\)就没打,特殊性质也没管,事后发现贪心就是正解,嘎

T3根本不会,狂打特判骗分,然后回去看T1,推了个特殊性质,之后就没啥实质性操作了

出分65+45+0=110,再次归于平庸

T3白给的第一个点忘了\(return 0\),答案小于4我直接输出4,真就一分不得

T1.入阵曲

\(n_4\)暴力很显然,关键是怎么优化

前缀和肯定还是有的,推一些其他的性质

如果我们把问题简化到一维,那么现在我们已经有了前缀和,那么一个矩形就是两个前缀和相减,一个关键的操作出现了:

取模.

我们把前缀和对k取模,注意到只有两个余数相同的数才有相减模k是0,也就是k的倍数,那么问题就转化成了对于每个余数统计出现的次数,解决

扩展到二维依然是这个思路,我们用循环枚举列数,用一维的思想处理每个行,最多只有\(n^2/2\)行,暴力统计即可,\(n_3\)过400没问题

一个细节是统计的时候,朴素的统计方法是对每个余数出现的次数x,累加\(n\times(n-1)/2\),但这样涉及到反复查找和清空的问题,用哈希表或者stl都会浪费大量时间,一个好的做法是把这个式子拆成等差数列,每次遇到,先更新答案再累加cnt值,就可以保证复杂度了

#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[500][500],b[500][500],c[500][500],d[500][500];
int n,m,kk;
inline int get(int x1,int y1,int x2,int y2)
{
return d[x2][y2]-d[x1-1][y2]-d[x2][y1-1]+d[x1-1][y1-1];
}
int f[1000005];
stack <int> s;
signed main()
{
// freopen("data.out","r",stdin);
cin>>n>>m>>kk;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]=b[i][j-1]+a[i][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
c[i][j]=c[i][j-1]+b[j][i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
d[i][j]=c[j][i];
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
{
f[0]=1;
for(int k=1;k<=m;k++)
{
int p=get(j,1,i,k)%kk;
ans+=f[p];f[p]++;s.push(p);
}
while(!s.empty())
{
f[s.top()]=0;
s.pop();
}
}
cout<<ans;
return 0;
}

注意0比较特殊,本身就能贡献答案,所以要先加1,清空最好用个栈,避免不必要浪费

一定要开\(longlong\),不然会死

T2.将军令

树形dp可以分别过1,2,3,越往后越复杂,感觉考场写出2就了不起了,(其实最多到4),但是这个有通用dp,详见树形dp强者Yubai

其实正解是个贪心,对于最深的点,控制它的最优方案一定是驻扎在他的k级祖先,理解一下,如果往上就看不住他了,往下的话,相当于哨兵的一部分守卫半径浪费了,也一定不会更优,所以这个贪心是对的

那么事情就变得简单了,按深度从大到小,依次看它有没有被看守,如果没有,就在它的k级祖先驻扎,然后通过暴力修改它周边点的状态,复杂度是\(kn\)的,搜索祖先时可以记忆化,不过貌似更慢,修改状态的时候用一个临时数组标记一下,防止重复造成死循环

#include <bits/stdc++.h>
using namespace std;
struct node{
int from,to,next;
}a[200005];
int head[100005],mm=1;
inline void add(int x, int y)
{
a[mm].from=x;a[mm].to=y;
a[mm].next=head[x];head[x]=mm++;
}
int n,k,t;
bool v[100005];int d[100005];
bool l[100005];
queue <pair<int,int> > q;
inline int getk(int x,int p)
{
if(p==0)return x;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;
if(d[y]>=d[x])continue;
return getk(y,p-1);
}
return x;
}
void bfs()
{
v[1]=1;
q.push(make_pair(1,1));
while(!q.empty())
{
int x=q.front().first,sb=q.front().second;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;
if(v[y])continue;
v[y]=1;
q.push(make_pair(y,sb+1));
}
d[x]=sb;q.pop();
}
}
stack <int> s;
inline void gan(int x,int p)
{
v[x]=1;s.push(x);l[x]=1;if(p==0)return;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;
if(v[y])continue;
gan(y,p-1);
}
}
inline void clear()
{
while(!s.empty())
{
v[s.top()]=0;
s.pop();
}
}
struct ga{
int bfn ,x;
}b[100005];
bool com(ga x,ga y)
{
return x.bfn>y.bfn;
}
signed main()
{
// freopen("data.out","r",stdin);
// freopen("so.out","w",stdout);
cin>>n>>k>>t;
for(int i=1;i<=n-1;i++)
{
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
if(k==0)
{
cout<<n;
return 0;
}
bfs();memset(v,0,sizeof(v));
for(int i=1;i<=n;i++)
{
b[i].bfn=d[i];
b[i].x=i;
}
sort(b+1,b+1+n,com);
int ans=0;
for(int i=1;i<=n;i++)
{
if(l[b[i].x])continue;
ans++;gan(getk(b[i].x,k),k);
clear();
}
cout<<ans;
return 0;
}

感觉写的好丑

T3.星空

乍一看貌似很不可做,这个题的精髓就在于转化问题

关键是要明白差分,对于难搞的区间反转,我们可以用差分实现

原序列亮是0,不亮是1(这么定义后面会方便),对于原数组a,差分数组b就是

\(b_i=a_i\oplus a_{i+1}\)

可以类比\(int\)的差分,就是不进位加法,那么对于每个a数组中的数,就可以表示为

\[a_x=\sum_i^{x-1}b_i(\oplus)
\]

这里求和是异或和,那么区间修改\(l\)到\(r\)就变成了单点修改\(l-1\)和\(r\)

最终结果是整个序列全是0,我们完成了第一次转化

我们分析一下,对于一次修改,有三种情况:

1.两边全是0

2.两边全是1

3.一个1一个0

显然全是0没有意义,白白增加工作量,全是1的可以都变成0,是我们想要的操作,一个1一个0的话,由于是反转,就把他们交换了位置,相当于把一个1移动了一个区间的长度,我们目标就是把他们都消去,这里引用一下出题人的描述

给定一个有n个点的图,只有不超过2k个点有物品,每次从m中距离中选择一种移动,两个物品碰到一起会消去,求最少操作次数

我们发现消去2个固定的1的最小步数是恒定的,可以BFS,全部预处理出来,然后至于该怎么选,就是一个裸的状压dp了

为啥是状压?看见小数据,你还能干啥?

#include <bits/stdc++.h>
using namespace std;
bool a[40050];int b[100];
bool c[40050];
vector <int> s;
int n,m,k;
int d[40050],dis[20][20];
queue <pair<int,int>> q;
void bfs(int x)
{
memset(d,0x3f,sizeof(d));
q.push(make_pair(x,0));
d[x]=0;
while(!q.empty())
{
int p=q.front().first,v=q.front().second;
for(int i=1;i<=m;i++)
{
int bi=b[i];
if(p-bi>=0&&d[p-bi]>v+1)d[p-bi]=v+1,q.push(make_pair(p-bi,v+1));
if(p+bi<=n&&d[p+bi]>v+1)d[p+bi]=v+1,q.push(make_pair(p+bi,v+1));
}
q.pop();
}
}
inline int getsum(int x)
{
int ans=0;
while(x)
{
if(x&1)ans++;
x>>=1;
}
return ans;
}
vector <int> sta[17];
int f[1<<17];
signed main()
{
cin>>n>>k>>m;
for(int i=1;i<=k;i++)
{
int x;scanf("%d",&x);
a[x]=1;
}
for(int i=1;i<=m;i++)scanf("%d",&b[i]);
for(int i=0;i<=n;i++)c[i]=a[i]^a[i+1];
for(int i=0;i<=n;i++)
if(c[i])s.push_back(i);
memset(dis,0x3f,sizeof(dis));
for(int i=0;i<s.size();i++)
{
bfs(s[i]);
for(int j=0;j<s.size();j++)
dis[i][j]=d[s[j]];
}
int kk=s.size();
for(int i=0;i<(1<<kk);i++)
sta[getsum(i)].push_back(i);
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=0;i<kk;i+=2)
for(int j=0;j<sta[i].size();j++)
{
int x=sta[i][j];
if(f[x]>1e7)continue;
for(int p=0;p<kk;p++)
{
if(x&(1<<p))continue;
for(int t=0;t<kk;t++)
{
if(x&(1<<t))continue;
if(t==p)continue;
f[x|(1<<p)|(1<<t)]=min(f[x|(1<<p)|(1<<t)],f[x]+dis[p][t]);
}
}
}
cout<<f[(1<<kk)-1];
return 0;
}

BFS的时候要在进队之前更新d数组,否则增加很多复杂度,狂T不止

考试总结

1.静心思考,相信自己的判断,要有勇气写正解

2.注意细节,别人都能骗到的分你挂了,就说明你有差距,少一份也是少

3.开阔眼界,学会从做过的题里面找规律

noip模拟10的更多相关文章

  1. NOIP模拟 10

    (果然题目描述越人畜无害,题目难度越丧心病狂) (感觉T2大大锻炼了我的码力) T1 辣鸡 看见自己作为题目标题出现在模拟赛中,我内心无比激动 看完题面,一个N^2暴力思路已经成形 然后开始拼命想正解 ...

  2. 19.7.29 NOIP模拟10

    话说这次三道考试题直接可以连成一个段子:我一个辣鸡,连模板都不会打,只能跪倒在大佬面前; T1 辣鸡 但是我实在是太辣鸡了,最后干的T1,时间不够用,连暴力都没打对,无奈之下交了一个qj程序,60分( ...

  3. noip模拟10[入阵曲·将军令·星空](luogu)

    对于这次考试来说,总体考得还是不错的 就是有一个小问题,特判一定要判对,要不然和不判一样,甚至错了还会挂掉30分 还有一个就是时间分配问题,总是在前几个题上浪费太多时间,导致最后一个题完全没有时间思考 ...

  4. [考试总结]noip模拟10

    不小心有咕掉了一段时间 这次考试咕掉的分数也是太多了 然后就是这次暴力完全没有打满 遗憾啊遗憾 T1 入阵曲 前面的题目背景故意引导我们去往矩阵快速幂的方向去想 然而半毛钱关系没有 其实就是维护前缀和 ...

  5. Noip模拟10 2021.6.27

    T1 入阵曲 好了,又一个考试败笔题. 也就是在那个时候,小 F 学会了矩阵乘法.让两个矩阵乘几次就能算出斐波那契数, 真是奇妙无比呢. 不过, 小 F 现在可不想手算矩阵乘法--他觉得好麻烦.取而代 ...

  6. 2021.6.29考试总结[NOIP模拟10]

    T1 入阵曲 二位前缀和暴力n4可以拿60. 观察到维护前缀和时模k意义下余数一样的前缀和相减后一定被k整除,前缀和维护模数,n2枚举行数,n枚举列, 开一个桶记录模数出现个数,每枚举到该模数就加上它 ...

  7. 7.30 NOIP模拟10

    T1.辣鸡 考试的时候竟然被我以“麻烦”弃掉了,赛后发现这题好水啊,直接sort一下寻找四周即可. T2.模板 考试时期望得分70,实际得分5 首先看到这种题基本就是线段树,我们以时间为下标,对每一个 ...

  8. NOIP 模拟 10 考试总结

    T1 一道很妙的题,打暴力分也很多,但是考试的时候忘开 long long 了. 题解 T2 一道挺水的题,不过...(打挂了) 题解 T3 此题甚妙,转化真多,不过对思维是一个非常大的扩展 题解 考 ...

  9. 10.17 NOIP模拟赛

    目录 2018.10.17 NOIP模拟赛 A 咒语curse B 神光light(二分 DP) C 迷宫maze(次短路) 考试代码 B 2018.10.17 NOIP模拟赛 时间:1h15min( ...

随机推荐

  1. 关于varnish缓存

    目录 缓存的概念 一.varnish缓存 1. 简介 2. 总体结构 2.1 两个主进程 2.1.1 Management进程 2.1.2 Child/Cacher进程 2.2 Varnish的日志收 ...

  2. 学C记录(理解递归问题之汉诺塔)

    汉诺游戏规则如下: 1.有三根相邻的柱子,标号为A,B,C. 2.A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘. 3.现在把所有盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘 ...

  3. C语言:FILE p和FILE *p

    FILE p和FILE *p大概可以这么理解:1 . 前一个p指文件型变量,后一个p指文件地址型变量.2 . 前一个p的内存地址已定,后一个p内存地址未定. 前一个是声明类对象,后一个是声明一个可指向 ...

  4. vs2013:asp.net网站发布

    1."生成"菜单"生成网站" 2."发布网站" 3.配置文件--自定义,名称 4.发布方法:文件系统,确定目标位置(另外的) 5.配置选择r ...

  5. python numpy高效体现

    import numpy as np import time def python_sum(n): a=[i**2 for i in range(n)] b=[i**3 for i in range( ...

  6. odoo14在列表视图里添加自定义按钮

    static/js/xxxx.js 这里定义按钮odoo.define('add.tree.view.buttons', function (require) { "use strict&q ...

  7. odoo14--odoo前端框架owl【odoo web library】

    原文链接:https://www.alanhou.org/odoo-14-owl-todolist/ 1.组件树 Root         /   \        A     B       / \ ...

  8. 利用C++11可变模板,封装调用dll导出函数

    起因 开发中经常需要动态调用一些导出函数,试着利用C++11特性封装一下 尝试 常规使用 typedef int WINAPI (*TMessageBoxA)(HWND hWnd,LPCSTR lpT ...

  9. 用Python预测双色球福利彩票中奖号码(请不要当真)

    前言 双色球是中国福利彩票的一种玩法. 红球一共6组,每组从1-33中抽取一个,六个互相不重复.然后蓝球是从1-16中抽取一个数字,这整个组成的双色球 python从零基础入门到实战 今天,我们就用P ...

  10. C# Get和Post请求接口类

    public class HttpHelper {/// <summary> /// Get请求 /// </summary> /// <param name=" ...