自适应树游走协议
题目描述:
在计算机网络中,当多个站点试图通过同一个信道同时进行传输时,传输的数据会出现乱码,这称为冲突。自适应树游走协议(Adaptive Tree Walk Protocol**)是一种避免冲突的方法。它将所有使用同一个信道的站点使用一棵完全二叉树组织起来,每个叶子**代表一个站点。
当有站点想要使用信道时,使用深度优先遍历的方法处理冲突:在第一个时隙检查树根,若其所有后代结点中只有唯一的结点请求信道,则直接将信道分配给它;否则依次递归检查左子树、右子树,直到不存在冲突为止。
图中展示了A-H八个共享信道的站点被组织的方式。若某一时刻C、D、H三个站点同时请求信道,则该协议处理冲突如下:
- 第一个时隙检查0号结点,它有三个后代C、D、H均发起了请求,存在冲突。
- 第二个时隙检查0号结点的左儿子,即1号结点。它有两个后代C、D均发起了请求,存在冲突。
- 第三个时隙检查1号结点的左儿子,即3号结点。它没有后代发起请求,不需要继续向下递归检查。
- 第四个时隙检查1号结点的右儿子,即4号结点。它有两个后代C、D发起了请求,存在冲突。
- 第五个时隙检查4号结点的左儿子,即C。它是发起了请求的叶子结点,因此该时隙将信道分配给C。
- 第六个时隙检查4号结点的右儿子,即D。它是发起了请求的叶子结点,因此该时隙将信道分配给D。此时,对1号结点的递归检查已经完成。
- 第七个时隙检查0号结点的右儿子,即2号结点。它只有一个后代H发起请求,因此该时隙将信道分配给H,不需要继续向下递归检查。
处理所有请求共耗费了七个时隙。
作为一名刚刚开始学习计算机的萌新,Miaoyao为自己掌握了这个协议而非常得意。于是,他想要来考考你:给出站点个数n以及一些发起请求的站点编号,他想知道该协议需要几个时隙处理完所有请求。
当然,由于Miaoyao非常菜,他不希望把问题变得太过复杂。因此,他保证n是2的整数幂,即使用二叉树组织起来时,所得到的将会是一棵满二叉树:所有叶子均在同一层。同时,尽管针对这个协议有很多行之有效的优化,但是Miaoyao并没有掌握,他只会按照上面所述的流程逐个检查。
思路1:
用前缀和+差分去实现区间值的查询,然后便于去dfs,有点线段树的感觉,写起来也很简短
#include<map>
#include<set>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<bitset>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<limits.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
//不开longlong见祖宗!
int n, k, x;
int tr[MAX];
int sum[MAX];
int dfs(int l, int r){
if(sum[r] - sum[l - 1] <= 1)return 1;
return 1 + dfs(l, (l + r) / 2) + dfs((l + r) / 2 + 1, r);
}
int main(){
cin>>n>>k;
for(int i = 1; i <= k; ++i){
cin>>x;
++tr[x];
}
for(int i = 1; i <= n; ++i)sum[i] = sum[i - 1] + tr[i];
cout<<dfs(1, n)<<endl;
return 0;
}
思路2:
也就是我在比赛的时候用的方法
先对0到n-2的点做一次预处理,目的是求每个点的子孙中有几个需要分配信道的点,用数字记录下来,然后再去dfs
#include<map>
#include<set>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<bitset>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<limits.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
//不开longlong见祖宗!
int n, k, x;
int tr[MAX];
bool judge(int x){//判断是否出界
if(x >= n * 2 - 1 || x < 0)return false;
else return true;
}
int cnt;
void check(int x){//判断一个节点有几个需要分配信道的子节点
if(!judge(x))return;
if(tr[x] == 1){
++cnt;
return;
}
check(x * 2 + 1);
check(x * 2 + 2);
}
void yuchuli(){
for(int i = 0; i < n - 1; ++i){
cnt = 0;
check(i);
tr[i] = cnt;
}
}
int ans;
void dfs(int x){
if(!judge(x))return;
++ans;
if(tr[x] <= 1)return;
dfs(x * 2 + 1);
dfs(x * 2 + 2);
}
int main(){
cin>>n>>k;
for(int i = 1; i <= k; ++i){
cin>>x;
tr[x + n - 2] = 1;
}
yuchuli();
dfs(0);
cout<<ans<<endl;
return 0;
}
困难的数学题
题目描述:
Miaoyao好不容易从Dzerzhinski的打击中缓过气来。他潜心学习数学,终于小有所成,于是他给你出了一道自认为非常困难的数学题:
给定正整数n,将其分解为若干个不小于k的正整数之和,有多少种方案?(顺序不同的划分也视为不同的方案)
由于答案可能很大,你只需要输出它对109+710^9+7109+7取模的结果即可。
思路:
dp的那个思路我至今无法理解:
f[i] => 组成正整数i的方案数
f[i]对f[i + 1]、f[i + 2] …… 都可以产生贡献,但是为什么递推式子是f[i] = f[i - 1] + f[i - k]??
所以这里我给出另一种关于组合数的解法
主要利用的是挡板法
对于n,我们就当成n个1,然后分成m组(1<=m<=n/k)
正常的挡板法分成的m块数量要大于等于1
现在是大于等于k,这个时候我们要把k变成1,也就是让每一块都减去k-1,一共m块,就是让n-m * (k-1),然后再插入m-1个挡板就行
公式为:
这个题还需要进行组合数取模,套个板子即可
#include<map>
#include<set>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<bitset>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<limits.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 1000000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
ll n, k, m, q;
ll ans;
const ll Mod=1e9 + 7;
ll fac[1000100];//存阶乘
ll inv[1000100];//存逆元
ll quick(ll a,int b)//快速幂算法
{
ll ans=1;
while(b)
{
if(b&1)ans=(ans*a)%Mod;
a=a*a%Mod;
b>>=1;
}
return ans;
}
void getfac()
{
fac[0]=inv[0]=1;
for(int i=1;i<=1000000;i++)
{
fac[i]=fac[i-1]*i%Mod;
inv[i]=quick(fac[i],Mod-2);
}
}
ll getans(ll n,ll m)
{
return fac[n]*inv[n-m]%Mod*inv[m]%Mod;
}
int main(){
getfac();
cin>>n>>k;
m = n / k;
for(int i = 1; i <= m; ++i){
q = n - i * (k - 1);
ans += getans(q - 1, i - 1);
// ans += C(p - 1, i - 1);
ans %= mod;
}
ans %= mod;
cout<<ans<<endl;
return 0;
}
平衡的字符串
题目描述:
lhl322告诉Miaoyao,仅仅学好数学是不够的。想要在XCPC中取得好成绩,字符串类的题目就绕不开。于是,Miaoyao又开始研究起字符串来了……
对于一个01串,若其中0的个数与1的个数相同,我们称它是平衡的。
给出由0、1、?三种字符组成的01串。Miaoyao想知道是否存在一种方案,将每个'?'均改变为0或1中的一种,使得所有长度为k的子串都是平衡的。
思路:
首先k为奇数显然无解
其次对于[1,k] [2,k + 1],其交集是[2,k], 如果这两个串都是平衡的,则s[1]必然等于s[k + 1],其他的也同理
也就是Si = Si + k
于是我们就可以根据下标分成k组,每组只要确定一个元素,那么这个组都必然是这个元素,如果一个组内出现不同的,就说明不符合
再就是要对前k个字符进行判断,只有当1和0的数量都小于等于k/2才行
#include<map>
#include<set>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<bitset>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<limits.h>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '\n'
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
//不开longlong见祖宗!
int n, k, p;
int num1, num2;
int zero, one;
string s;
int main(){
cin>>n>>k>>s;
if(k % 2 == 1)cout<<"No\n";
else {
for(int i = 0; i < k; ++i){
zero = 0;one = 0;
for(int j = i; j < n; j += k){
if(s[j] == '1')++one;
else if(s[j] == '0')++zero;
}
if(one && zero){
cout<<"No\n";
return 0;
}
if(zero)++num1;
if(one)++num2;
}
if(num1 <= k / 2 && num2 <= k / 2)cout<<"Yes\n";
else cout<<"No\n";
}
return 0;
}

京公网安备 11010502036488号