NOIp膜你题   Day2

duliu 出题人:ZAY   

 题解

这就是一道组合数问题鸭!!!  可是泥为什么没有推出式子!!

首先我们知道的是 m 盆花都要摆上,然后他们的顺序不定(主人公忘记了)

所以初步得到一个排列数 P( m,m ) , 即 Pmm

那么我们就还剩下 n-m 个空位置,这些空位置都是不可以放花的,于是我们逆向思维一下:

n-m  个位置不放花,也就是可以在这些位置周围插空放花,把这些位置隔开,那么就可以把m盆花放到 n-m+1 个空里,由于这是对于空位置来说的,没有顺序可言,每个都是一毛一样的,所以得到一个组合数  C(n-m+1 , m) , 即 Cn-m+1m

所以 ans = P( m,m ) * C( n-m+1 , m )

= m! * (n-m+1)! / [ m! * (n-m+1 - m)! ]

化简一下就是下面

=(n-m+1)*(n-m+1-1)*......*(n-2m+2)

注意

ans 要开 long long , 试过毒了,数据不开 long long 会炸

代码

#include<bits/stdc++.h>

using namespace std;

int type,n,m,p;
long long ans=; int main()
{
freopen("ilove.in","r",stdin);
freopen("ilove.out","w",stdout); scanf("%d%d%d%d",&type,&n,&m,&p); if(n==&&m==) { printf("1\n"); return ; } int a=n-m+,b=n-m-m+;
for(int i=a;i>=b;i--)
ans=(ans%p*i%p)%p; printf("%ld\n",ans);
return ; }

看看出题人的题解

一般这种数数题,也就是求方案数,有两种解决方案:DP&组合数学

1.DP

2.组合数学

Ps:插空法就是我上面题解里面提到的


题解

注意到根据题目规定的走法,在进入一个节点以后,必须遍历完它的整个子树, 否则一旦离开这个节点,再也无法进入这棵子树,从而导致该节点的某个孩子没能放 上石子,导致这个节点不能放上石子。

同时又有每个节点放上石子以后,它的子树的 石子可以全部取回。设在节点 u 放石子需要有 ansu 个石子,则放完 u 以后可以取回 ansu-wu 个石子。

因为你准备了ansu个石子,u点需要wu个石子放上,那么放完之后,他的子树上的石子就可以取回来了,也就是ansu-wu个,这些也就是节点u的孩子所需石子数和。

于是考虑影响问题答案的显然是从 u 进入每个孩子的顺序,由于最多有两个孩 子,直接比较一下就可以知道先进入哪个孩子更优秀了。时间复杂度 O(n)

代码

#include <cstdio>
#include <vector>
#include <algorithm> const int maxn = ; int n;
int MU[maxn], ans[maxn]; //MU也就是W
std::vector<int>son[maxn]; void dfs(const int u);
bool cmp(const int &_a, const int &_b); int main() {
freopen("yin.in", "r", stdin);
freopen("yin.out", "w", stdout); scanf("%d", &n);
for (int i = , x; i <= n; ++i) {
scanf("%d", &x);
son[x].push_back(i);
}
for (int i = ; i <= n; ++i) {
scanf("%d", MU + i);
//数组名+一个东西 这是指针的写法
//相当于scanf("%d",&MU[i]);
}
dfs();
for (int i = ; i < n; ++i) {
printf("%d ", ans[i]);
}
printf("%d\n", ans[n]);
return ;
} void dfs(const int u) {
for (auto v : son[u]) { //遍历u的所有子节点
dfs(v);
}
std::sort(son[u].begin(), son[u].end(), cmp);
//按照差值不升序排序
int _ret = ;
//此时U节点的所有子节点已经算出来了,准备计算U节点
for (auto v : son[u]) { //叶节点没有儿子就不会for循环
if (_ret >= ans[v]) //上一个子节点收回的石子足够摆当前节点,就摆上
{
_ret -= ans[v]; //ret摆完之后更新
}
else
{
ans[u] += ans[v] - _ret; //收回的石子不够放了,往上补
_ret = ans[v] - MU[v];
//遍历完U的儿子V节点对应的子树后,收回除了 U的儿子v以外所有节点上的石头
}
}
ans[u] += std::max(, MU[u] - _ret);
//深搜,会先到达所有叶节点,并且所有叶节点的ans=w[i]
} inline bool cmp(const int &_a, const int &_b) {
return (ans[_a] - MU[_a]) > (ans[_b] - MU[_b]);
}

题解

1.可以通过前4个任务点的代码QWQ

因为我们看到每封信最多只会有30个字符对吧,那么每次询问给出一个询问区间的时候,我们就可以比对每一位的字符

比如我们比对到了第 i 位,询问区间从头到尾枚举看一看,对于这一位,有几个0,几个1,几个?

如果既有0又有1,那么显然是无解的

如果只有0和?,那么这一位只可以为0,也就是?只能被填成0

如果只有1和?,同上

如果全是?,那么既可以填0,又可以填1,也就是有2种方案

我们把所有位都枚举了一遍,那么可能会出现结果:无解,有唯一解,有解但不唯一;对于最后一种情况显然是?的数目影响的,乘法分布原理 2cnt  , cnt表示?的数目

代码

45‘代码   后边超时辣

#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath> using namespace std; string s[],s1;
int n,m,q;
int qus,num,l,r;
long long ans=; int main()
{
freopen("lin.in","r",stdin);
freopen("lin.out","w",stdout);
scanf("%d%d%d",&n,&m,&q); if(q==) return ; for(int i=;i<=m;i++)
cin>>s[i]; for(int i=;i<=q;i++)
{
ans=;
scanf("%d",&qus);
if(qus==)
{
scanf("%d%d",&l,&r); int flag=,cnt=; for(int j=;j<n;j++)
{
if(flag==) break;
bool glf=;
char mp;
mp=s[l][j];
if(mp!='?') glf=;
else glf=;
for(int k=l+;k<=r;k++)
{
if(s[k][j]!='?')
{
if(mp=='?')
{
glf=;
mp=s[k][j];
continue;
}
if(mp!='?'&&mp!=s[k][j])
{
flag=;
continue;
} } else if(s[k][j]=='?')
{
if(glf==) glf=;
} }
if(mp!='?') glf=;
cnt+=glf;
} if(flag==) {printf("0\n");}
else
{
ans=pow(,cnt);
printf("%ld\n",ans); }
}
if(qus==)
{
scanf("%d",&num);
cin>>s1;
s[num]=s1;
}
} return ;
}

我百分之200的错误都是因为脑残,下面这个是整理了一下(貌似并不可以多得分)

#include<bits/stdc++.h>

using namespace std;

int n,m,q;
string s[],s1;
int opt,l,r,pos; inline int read()
{
int ans=;
char last=' ',ch=getchar();
while(ch<''||ch>'') last=ch,ch=getchar();
while(ch>=''&&ch<='') ans=ans*+ch-'',ch=getchar();
if(last=='-') ans=-ans;
return ans;
} int main()
{
freopen("lin.in","r",stdin);
freopen("lin.out","w",stdout); n=read(); m=read(); q=read();
for(int i=;i<=m;i++)
cin>>s[i]; for(int i=;i<=q;i++)
{
opt=read();
if(opt==)
{
pos=read();
cin>>s1;
s[pos]=s1;
}
else
{
l=read();r=read(); long long ans;
int flag=,cnt=; for(int j=;j<n;j++)
{
if(flag==) break;
int flag0=,flag1=,flag2=;
for(int k=l;k<=r;k++)
{
if(s[k][j]=='') flag0++;
if(s[k][j]=='') flag1++;
if(s[k][j]=='?') flag2++;
} if(flag0!=&&flag1!=) { flag=; continue; }
else if(flag2==(r-l+)) cnt++; } if(flag==) ans=;
else ans=pow(,cnt);
printf("%ld\n",ans); }
} return ;
}

当然也可以用按位与维护

2.考虑正解:线段树

代码

忍不了这个指针了

#include <cstdio>

template <typename T>

inline void qr(T &x) {
char ch = getchar(), lst = ' ';
while ((ch > '') || (ch < '')) lst = ch, ch=getchar();
while ((ch >= '') && (ch <= '')) x = (x << ) + (x << ) + (ch ^ ), ch = getchar();
if (lst == '-') x = -x;
} //快读 const int maxn = ; int n, m, q;
char s[maxn][]; //每封信的内容 #ifdef ONLINE_JUDGE
int Ans;
#endif struct Tree {
Tree *ls, *rs;
int l, r, x, y;
bool leg; //是否合法 Tree() {
ls = rs = NULL;
l = r = x = y = ;
leg = true; //先假定合法
} void pushup() { //构造a1,a2,b1,b2
if (!(this->ls->leg && this->rs->leg)) {
this->leg = false;
} else {
if ((this->ls->x & this->rs->x) & (this->ls->y ^ this->rs->y)) {
this->leg = false;
} else {
this->leg = true;
this->x = this->ls->x | this->rs->x;
this->y = this->ls->y | this->rs->y;
}
}
}
};
Tree *rot; //线段树的根 void ReadStr(char *p); //指针 快读
void Update(const int x);
void Query(const int l, const int r);
void update(Tree *const u, const int p); //修改
Tree query(Tree *u, const int l, const int r); //查询
void build(Tree *const u, const int l, const int r); //建树 int main() {
freopen("lin.in", "r", stdin);
freopen("lin.out", "w", stdout);
qr(n); qr(m); qr(q);
for (int i = ; i <= m; ++i) {
ReadStr(s[i] + );
}
build(rot = new Tree, , m); //建树
int opt, l, r;
while (q--) {
opt = ; qr(opt);
if (opt == ) { //查询
l = r = ; qr(l); qr(r);
Query(l, r);
} else {
l = ; qr(l);
ReadStr(s[] + );
Update(l); //修改第一个
}
}
#ifdef ONLINE_JUDGE //鬼知道干啥的
printf("%d\n", Ans);
#endif
return ;
} void ReadStr(char *p) {
do *p = getchar(); while ((*p != '') && (*p != '') && (*p != '?'));
do *(++p) = getchar(); while ((*p == '') || (*p == '') || (*p == '?'));
*p = ;
} void build(Tree *const u, const int l, const int r) {
if ((u->l = l) == (u->r = r)) { //当前区间在要建的区间内
for (int i = ; i <= n; ++i) {
if (s[l][i] != '?') {
u->x |= << i;
if (s[l][i] == '') {
u->y |= << i;
}
}
}
} else { //不在
int mid = (l + r) >> ;
build(u->ls = new Tree, l, mid);
build(u->rs = new Tree, mid + , r);
u->pushup();
}
} Tree query(Tree *u, const int l, const int r) {
if ((u->l > r) || (u->r < l)) return Tree(); //当前区间完全不在查询区间
if ((u->l >= l) && (u->r <= r)) return *u; //完全在
Tree _ret;
auto ll = query(u->ls, l, r), rr = query(u->rs, l, r); //否则递归来求
_ret.ls = &ll; _ret.rs = &rr;
_ret.pushup();
return _ret;
} void Query(const int l, const int r) {
auto _ret = query(rot, l, r);
if (!_ret.leg) { //不合法输出‘0’
#ifndef ONLINE_JUDGE
puts("");
#endif
} else {
int ans = ;
for (int i = ; i <= n; ++i) if (!(_ret.x & ( << i))) {
ans <<= ;
}
#ifdef ONLINE_JUDGE
Ans ^= ans;
#else
printf("%d\n", ans);
#endif
}
} void update(Tree *u, const int p) { //修改第p个
if (u->ls) { //寻找p
if (u->ls->r >= p) { //左子树右端点比p大
update(u->ls, p);
} else {
update(u->rs, p);
}
u->pushup(); //把子节点的信息合并到父节点
} else {
*u = Tree();
u->l = u->r = p;
for (int i = ; i <= n; ++i) {
if (s[][i] != '?') {
u->x |= << i;
if (s[][i] == '') {
u->y |= << i;
}
}
}
}
} void Update(const int x) {
update(rot, x); //修改
}

拒绝理解Zay的代码

放一只water lift的代码(线段树板子)

#include <bits/stdc++.h>
using namespace std;
template <class T>
inline void read(T &num)
{
bool flag = ;
num = ;
char c = getchar();
while ((c < '' || c > '') && c != '-')
c = getchar();
if (c == '-')
{
flag = ;
c = getchar();
}
num = c - '';
c = getchar();
while (c >= '' && c <= '')
num = (num << ) + (num << ) + c - '', c = getchar();
if (flag)
num *= -;
}
template <class T>
inline void output(T num)
{
if (num < )
{
putchar('-');
num = -num;
}
if (num >= )
output(num / );
putchar(num % + '');
}
template <class T>
inline void outln(T num)
{
output(num);
putchar('\n');
}
template <class T>
inline void outps(T num)
{
output(num);
putchar(' ');
}
const int N = , M = ;
int n, m, q;
struct segment
{
char val[M];
bool all1[M * ];
bool all0[M * ];
void init(int node, int nl, int nr)//这是个正常的线段树的板子(虽然位置好像不大正常)
{
if (nl < nr)
{
int mid = (nl + nr) >> ;
init(node * , nl, mid);
init(node * + , mid + , nr);
all1[node] = all1[node * ] & all1[node * + ];
all0[node] = all0[node * ] & all0[node * + ];
}
else
{
if (val[nl] == '?')
all1[node] = all0[node] = ;
else
{
all1[node] = val[nl] == '';
all0[node] = val[nl] == '';
}
}
}
void modify(int node, int nl, int nr, int x, char va)
{
if (val[x] == va)
return;
if (nl < nr)
{
int mid = (nl + nr) >> ;
if (x <= mid)
{
modify(node * , nl, mid, x, va);
}
else
{
modify(node * + , mid + , nr, x, va);
}
all1[node] = all1[node * ] & all1[node * + ];
all0[node] = all0[node * ] & all0[node * + ];
}
else
{
if (va == '?')
all1[node] = all0[node] = ;
else
{
all1[node] = va == '';
all0[node] = va == '';
}
val[x] = va;
}
}
pair<bool, bool> query(int node, int nl, int nr, int l, int r)
{
if (l <= nl && r >= nr)
{
return make_pair(all1[node], all0[node]);
}
int mid = (nl + nr) >> ;
bool a1 = true, a0 = true;
if (l <= mid)
{
auto lo = query(node * , nl, mid, l, r);
a1 &= lo.first;
a0 &= lo.second;
}
if (r >= mid + )
{
auto lo = query(node * + , mid + , nr, l, r);
a1 &= lo.first;
a0 &= lo.second;
}
return make_pair(a1, a0);
}
void dfs(int node, int nl, int nr)
{
if (nl < nr)
{
int mid = (nl + nr) >> ;
dfs(node * , nl, mid);
dfs(node * + , mid + , nr);
}
outps(nl);
outps(nr);
outps(all1[node]);
outln(all0[node]);
}
} segs[N];
int main()
{
freopen("lin.in", "r", stdin);
freopen("lin.out", "w", stdout);
read(n);
read(m);
read(q);
char ch;
for (int i = ; i <= m; i++)
{
for (int j = ; j <= n; j++)
{
do
{
ch = getchar();
} while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\0');
segs[j].val[i] = ch;
}
}
for (int i = ; i <= n; i++)
{
segs[i].init(, , m);
}
while (q--)
{
bool opt;
read(opt);
if (opt == )
{
int l, r;
read(l);
read(r);
int ans = ;
for (int i = ; i <= n; i++)
{
auto lo = segs[i].query(, , m, l, r);
ans *= (lo.first + lo.second);
}
outln(ans);
}
else
{
int pos;
read(pos);
for (int i = ; i <= n; i++)
{
do
{
ch = getchar();
} while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\0');
segs[i].modify(, , m, pos, ch);
}
}
}
}

彩蛋:

考试暴露了很多小问题

1.文件的读写

安利一个方法:如何比较自己的代码ans和答案out是否一致(不仅是用在NOIP上,还有NOI,IOI)

(1)键盘按住 shift 键,同时鼠标右键单击

(2)会崩出一个会话框,点击“在此处打开命令窗口”,win10不大一样,好像是f**忘了

(3)就会出现这个东西

不输入cmd 也可以,因为它本身就是cmd

(4)输入  fc  文件1  文件2

如果不同的话

如果相同的话

这样你就可以看看自己的答案对不对,错在哪里QWQ

2.数据分治

也就是对于不同的子任务你可以用不同的方法解决掉,从而骗取更多分数

3.编译&加文件的顺序

大型考试的时候呢,如果你代码一旦进行了修改,那么一定要重新试一遍样例

freopen就不要注释掉啦

4.题目难度和顺序安排不一定有瓜

NOIP 膜你题 DAY2的更多相关文章

  1. NIOP 膜你题

    NOIp膜你题   Day1 duliu 出题人:ZAY    1.大美江湖(mzq.cpp/c) [题目背景] 细雪飘落长街,枫叶红透又一年不只为故友流连,其实我也恋长安听门外足音慢,依稀见旧时容颜 ...

  2. NOIp 2015真题模拟赛 By cellur925

    果然我还是最菜的==不接受反驳== Day1 T1:神奇的幻方 思路:直接模拟即可,由于当前放法只与上一放法有关系,用两个变量记录一下即可.10分钟内切掉== 预计得分:100分 实际得分:100分 ...

  3. [NOIP 2014复习]第三章:动态规划——NOIP历届真题回想

    背包型动态规划 1.Wikioi 1047 邮票面值设计 题目描写叙述 Description 给定一个信封,最多仅仅同意粘贴N张邮票,计算在给定K(N+K≤40)种邮票的情况下(假定全部的邮票数量都 ...

  4. NOIP提高真题整理(2011-2018)-标签

    加粗的后面应该会有相应的简单解析(如果没咕的话:)). 2011 day1 T1:铺地毯:逆着铺 T2:选择客栈:按颜色分类枚举+二分答案 T3:Mayan游戏:大模拟dfs+剪枝 day2 T1:计 ...

  5. Noip 2013 真题练习

    Day1 T1 转圈游戏 Link 一句话题意: 让你求 \({x + m \times 10^k} \bmod n\) 的结果. 直接套上快速幂的板子. code #include<iostr ...

  6. 备战NOIP每周写题记录(一)···不间断更新

    ※Recorded By ksq2013 //其实这段时间写的题远远大于这篇博文中的内容,只不过那些数以百记的基础题目实在没必要写在blog上; ※week one 2016.7.18 Monday ...

  7. 【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】

    比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; ...

  8. 2017北京国庆刷题Day2 afternoon

    期望得分:100+100+50=250 实际得分:100+70+50=220 T1 最大值(max) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK有一 ...

  9. 2017北京国庆刷题Day2 morning

    期望得分:100+100+40=240 实际得分:100+40+0=140 T1 一道图论神题(god) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK ...

随机推荐

  1. PAT 1080. Graduate Admission

    It is said that in 2013, there were about 100 graduate schools ready to proceed over 40,000 applicat ...

  2. 11. IDEA 在同一工作空间创建多个项目

    1.创建项目 二..创建工作空间 JavaWorkspace 1.File-> New Project -> 创建工作空间 JavaWorkspace,并 顺便创建项目 JavaOne 2 ...

  3. 游标后面select 带有in时

    今天遇到一个问题,使用游标时,在给游标填充值的时候,select  语句中带有 where查询条件,并且还有 in子句. 本来我是这样写的,试了很多次都不出结果,当然number in (304010 ...

  4. 移位运算>>与>>>

    无符号右移运算符 (>>>)右移表达式的位,不保留符号.result = expression1 >>> expression2>>>运算符把 e ...

  5. GitHub分支项目不支持搜索问题解决:Sorry, forked repositories are not currently searchable.

    错误如下: 在GitHub上的项目,如果是分支项目的星星数没有超过父项目,就不会去索引从而不会进行代码搜索. 解决方法: 1.要在搜索结果中包含分支,需要添加fork:true或fork:only查询 ...

  6. Huffman编码实现压缩解压缩

    这是我们的课程中布置的作业.找一些资料将作业完毕,顺便将其写到博客,以后看起来也方便. 原理介绍 什么是Huffman压缩 Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压 ...

  7. Php socket数据编码

    bytes.php  字节编码类 /** * byte数组与字符串转化类 * @author * created on 2011-7-15 */ class bytes { /** * 转换一个str ...

  8. luogu1082 同余方程

    题目大意:求$$ax\equiv 1(\ \mathrm{mod}\ m)$$的最小正整数解. 因为$ax-1|m$,故令$ax-1=-ym$,原方程就变成了$ax+my=1$.根据bezout定理此 ...

  9. xcode打包测试

    模拟器的内存cpu网络,都是电脑的.xcode可以查看. Xcode7之前是限制人,限制电脑,限制app,限制真机调试的. Xcode7之后,做真机测试只需要apple id即可,会自动生成证书. X ...

  10. 高效管理 Elasticsearch 中基于时间的索引——本质是在利用滚动模式做数据的冷热分离,热索引可以用ssd

    高效管理 Elasticsearch 中基于时间的索引 转自:http://stormluke.me/es-managing-time-based-indices-efficiently/ 用 Ela ...