Codeforces Round #788 (Div. 2)
Codeforces Round #788 (Div. 2)
E.Hemose on the Tree
题意:给定一颗节点个数为\(n=2^p\)个的树,需要给每条边和每个点赋值(1,2*n-1),每个数不重复,要求使得一条路径上的最大异或和最小的方案
做法:其实这个有点脑经急转弯的意思,因为发现其实不存在一种方案可以使得最大异或和小于n,那么是否能构成全部等于n的方案呢,这是可以的,并且很简单,那么题目就变成了需要构造最大疑惑和等于n的方案,根节点设为n,1和n+1,2和n+2,异或起来都是n,所以我们只需要把这些值分别赋值到边和点上就可以了,需要注意一下,路径上异或和为0时,n+i在边上,为n时,n+i在点上,这样保证所有异或和都小于等于n
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
int val[maxn],Next[maxn],Head[maxn],ver[maxn];
int edge[maxn];
int tot=-1,n,num;
void Insert(int u,int v){
ver[++tot]=v;
Next[tot]=Head[u];
Head[u]=tot;
}
void dfs(int x,int f,int e,int flag){
if(!val[x]) {
num++;
if(flag)val[x]=num+n,edge[e]=edge[e^1]=num;
else val[x]=num,edge[e]=edge[e^1]=num+n;
}
for (int i=Head[x];i!=-1;i=Next[i]){
if(ver[i]!=f){
dfs(ver[i],x,i,flag^1);
}
}
}
void init(){
tot=-1,num=0;
for (int i=0;i<=2*n;i++) val[i]=0,Next[i]=Head[i]=ver[i]=-1;
}
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
int T;
cin>>T;
while(T--){
int p;
scanf("%d",&p);
n=1<<p;
init();
for (int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
Insert(u,v);
Insert(v,u);
}
val[1]=n;
dfs(1,1,1,1);
printf("1\n");
for (int i=1;i<=n;i++) printf("%d ",val[i]);
printf("\n");
for (int i=0;i<=tot;i=i+2) printf("%d ",edge[i]);
printf("\n");
}
return 0;
}
F.Jee, You See?
题意:题意:求满足以下条件的 \(n\) 维向量 \(a=(a_1,a_2,a_3...a_n)\) 的个数:
- \(l<=a_1+a_2+a_3...a_n<=r\)
- \(a_1 xor a_2xora_3...a_n=X\)
做法:可以转化问题,若能求出满足以下条件的数量: - \(1<=a_1+a_2+a_3...a_n<=r\)
- \(a_1 xor a_2xora_3...a_n=X\)
用 \(r\) 对应的答案减去 \(l-1\) 对应的答案即可。
那么怎末考虑这个问题呢?我们按位来考虑,首先考虑暴力做法,总共最多就64位,枚举每一位可以选1,2,3...n个,但是这样的做法复杂度\(O(n^{64})\),考虑一下,怎么通过前几位的值来推现在的方案,而且又因为和还不能超过r,所以还需要知道当前位最多能枚举多少
考虑设\(dp[i][j]\),表示,考虑高位的前\(i\)位的时候,剩下\(j\)位可以用,那么当前位能用的最多是多少位呢?最多到\(2*j\)位,因为二进制中\(2^k\)和\(2*2^{k-1}\)的关系,那么前一位剩j位给我搞,我这一位不就最多能搞\(2*j\)位,但是如果r的当前位为1,还可以再加一位,所以设当前可以使用的位数为left所以转移方程\(dp[i][min(left-k,n)]=\sum_{k=0}^{k<=min(n,left)}dp[i+1][j]*C(n,k)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const ll mod=1e9+7;
ll dp[64][maxn],C[maxn][maxn];
ll solved(ll r,ll z,int n){
memset(dp,0,sizeof(dp));
dp[63][0]=1;
for (int i=62;i>=0;i--){
int add=(r>>i&1ll);
int par=(z>>i&1ll);
for (int j=0;j<=n;j++){
int left=j*2+add;
auto Minn = [=]{return n<left?n:left;};
for (int k=par;k<=Minn();k+=2){
int nowl=min(left-k,n);
dp[i][nowl]=(dp[i][nowl]+dp[i+1][j]*C[n][k]%mod)%mod;
}
}
}
ll ans=0;
for (int i=0;i<=n;i++){
ans+=dp[0][i];
ans%=mod;
}
//cout<<ans<<endl;
return ans%mod;
}
int main(){
#ifdef lmj_debug
freopen("1.in","r",stdin);
#endif
C[0][0]=1;
for (int i=1;i<=1000;i++){
for (int j=0;j<=i;j++){
if(j==0) C[i][j]=1;
else C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
ll n,l,r,z;
cin>>n>>l>>r>>z;
printf("%lld\n",((solved(r,z,n)-solved(l-1,z,n))%mod+mod)%mod);
return 0;
}