2025/3/29
赛时部分
RP++
希望别 MD 再出数学了
不是哥们四道数学有点恶心了吧
T1 看数据有 \(10^5\)
这个不用想搜索了
每个节点都有去父亲和留守两种选择
暴力搜索是 \(O(2^n)\) 的
所以我们需要找数学方法
先想如果树是一条链该怎么做
我们可以把每一种方案变成裸的 \(Go\)
因为 \(Stay\) 是没有分的
然后呢
然后我们美剧每一个节点作为父节点
可得分数乘以其余点的方案就行了
#include<bits/stdc++.h>
using namespace std;
//Data Part
typedef long long ll;
const int N=100099;
const ll Mod=1e9+7;
int n,val[N],fa[N];
ll ans=0;
vector<int> son[N];
//Chain Part
namespace Chain{
ll Qpow(ll d,ll x){
ll base=d,tmp=1;
while(x){
if(x&1)tmp=tmp*base%Mod;
base=base*base%Mod;
x>>=1;
}
return tmp;
}
ll cmp(int i){
if(i==1)return 0;
return 1;
}
void Solve(){
for(int i=1;son[i].size()>0;i=son[i][0]){
ans=Qpow(2,n-2-cmp(i))*(val[i]^val[son[i][0]])%Mod+ans;
ans%=Mod;
}
printf("%lld\n",ans);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=2;i<=n;i++)scanf("%d",&fa[i]),son[fa[i]].push_back(i);
for(int i=1;i<=n;i++)if(son[i].size()>1){goto NOT_A_CHAIN;}
Chain::Solve();
return 0;
NOT_A_CHAIN:
puts("Not a chain");
return 0;
}
现在我们拿到了 \(10\) 分
接下来我们发现二叉树的情况也很类似
我们只不过在一些地方有一点小修改
#include<bits/stdc++.h>
using namespace std;
//Data Part
typedef long long ll;
const int N=100099;
const ll Mod=1e9+7;
int n,val[N],fa[N];
ll ans=0;
vector<int> son[N];
//Chain Part
namespace Chain{
ll Qpow(ll d,ll x){
ll base=d,tmp=1;
while(x){
if(x&1)tmp=tmp*base%Mod;
base=base*base%Mod;
x>>=1;
}
return tmp;
}
ll cmp(int i){
if(i==1)return 0;
return 1;
}
void Solve(){
for(int i=1;son[i].size()>0;i=son[i][0]){
ans=Qpow(2,n-2-cmp(i))*(val[i]^val[son[i][0]])%Mod+ans;
ans%=Mod;
}
printf("%lld\n",ans);
}
}
//Binary Part
namespace BinTree{
ll Qpow(ll d,ll x){
ll base=d,tmp=1;
while(x){
if(x&1)tmp=tmp*base%Mod;
base=base*base%Mod;
x>>=1;
}
return tmp;
}
ll cmp(int i){
if(i==1)return 0;
return 1;
}
void Solve(int i=1){
if(son[i].size()==0)return;
if(son[i].size()==1){
ans=Qpow(2,n-2-cmp(i))*(val[i]^val[son[i][0]])%Mod+ans;
ans%=Mod;
}else{
ans=Qpow(2,n-3-cmp(i))*((val[i]^val[son[i][0]])+(val[i]^val[son[i][1]])+(val[i]^val[son[i][0]]^val[son[i][1]])+cmp(i)*(val[son[i][0]]^val[son[i][1]]))%Mod+ans;
ans%=Mod;
}
for(int it:son[i])Solve(it);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=2;i<=n;i++)scanf("%d",&fa[i]),son[fa[i]].push_back(i);
for(int i=1;i<=n;i++)if(son[i].size()>1){goto NOT_A_CHAIN;}
Chain::Solve();
return 0;
NOT_A_CHAIN:
for(int i=1;i<=n;i++)if(son[i].size()>2){goto NOT_A_BINARY_TREE;}
BinTree::Solve();
printf("%lld\n",ans);
return 0;
NOT_A_BINARY_TREE:
puts("not easy");
return 0;
}
这样就能 \(get\ 30\) 了
而我们打一个 10 分的暴力应该就 40 了
#include<bits/stdc++.h>
using namespace std;
//Data Part
typedef long long ll;
const int N=100099;
const ll Mod=1e9+7;
int n,val[N],fa[N];
ll ans=0;
vector<int> son[N];
//Chain Part
namespace Chain{
ll Qpow(ll d,ll x){
ll base=d,tmp=1;
while(x){
if(x&1)tmp=tmp*base%Mod;
base=base*base%Mod;
x>>=1;
}
return tmp;
}
ll cmp(int i){
if(i==1)return 0;
return 1;
}
void Solve(){
for(int i=1;son[i].size()>0;i=son[i][0]){
ans=Qpow(2,n-2-cmp(i))*(val[i]^val[son[i][0]])%Mod+ans;
ans%=Mod;
}
printf("%lld\n",ans);
}
}
//Binary Part
namespace BinTree{
ll Qpow(ll d,ll x){
ll base=d,tmp=1;
while(x){
if(x&1)tmp=tmp*base%Mod;
base=base*base%Mod;
x>>=1;
}
return tmp;
}
ll cmp(int i){
if(i==1)return 0;
return 1;
}
void Solve(int i=1){
if(son[i].size()==0)return;
if(son[i].size()==1){
ans=Qpow(2,n-2-cmp(i))*(val[i]^val[son[i][0]])%Mod+ans;
ans%=Mod;
}else{
ans=Qpow(2,n-3-cmp(i))*((val[i]^val[son[i][0]])+(val[i]^val[son[i][1]])+(val[i]^val[son[i][0]]^val[son[i][1]])+cmp(i)*(val[son[i][0]]^val[son[i][1]]))%Mod+ans;
ans%=Mod;
}
for(int it:son[i])Solve(it);
}
}
namespace BaoLi{
int tmp[30]={},cnt[30]={},pos;
void Solve(){
for(int i=0;i<=(1<<n)-2;i+=2){
for(int j=1;j<=n;j++)cnt[j]=0,tmp[j]=0;
for(int j=1;j<=n;j++){
if((i>>(j-1))&1)pos=fa[j];else pos=j;
tmp[pos]^=val[j],++cnt[pos];
}
for(int j=1;j<=n;j++){
if(cnt[j]>1)ans=(ans+tmp[j])%Mod;
}
}
printf("%lld",ans);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&val[i]);
for(int i=2;i<=n;i++)scanf("%d",&fa[i]),son[fa[i]].push_back(i);
for(int i=1;i<=n;i++)if(son[i].size()>1){goto NOT_A_CHAIN;}
Chain::Solve();
return 0;
NOT_A_CHAIN:
for(int i=1;i<=n;i++)if(son[i].size()>2){goto NOT_A_BINARY_TREE;}
BinTree::Solve();
printf("%lld\n",ans);
return 0;
NOT_A_BINARY_TREE:
if(n>20)goto NOT_OK;
BaoLi::Solve();
return 0;
NOT_OK:
puts("not easy");
return 0;
}
然后我们发现 T2 似乎可以再打一个 baoli
#include<bits/stdc++.h>
using namespace std;
//Basic Data Part
const int N=500098;
typedef long long ll;
int n,m,T,s,opt,color[N];
ll lastans=0,Mod;
//Line Part
struct node{
int i,d;
bool operator <(const node &rhs)const{
return d>rhs.d;
}
}top;
vector<node> k[N];
//Dijkstra Part
priority_queue<node> q;
int Distance[N],Bdist[N];
bool st[N];
void Dijkstra(){
for(int i=1;i<=n;i++)Distance[i]=INT_MAX;
for(int i=1;i<=666;i++)Bdist[i]=INT_MAX;
q.push({s,0}),Distance[s]=0;
while(!q.empty()){
top=q.top(),q.pop();
if(st[top.i])continue;
st[top.i]=1;
for(node it:k[top.i]){
if(Distance[it.i]>max(Distance[top.i],it.d)){
Distance[it.i]=max(Distance[top.i],it.d);
q.push({it.i,Distance[it.i]});
}
}
}
for(int i=1;i<=n;i++)Bdist[color[i]]=min(Bdist[color[i]],Distance[i]);
sort(Bdist+1,Bdist+601);
}
int Shit(int d){
int l=1,r=600,mid;
while(l<r){
mid=(l+r+1)>>1;
if(d<Bdist[mid])r=mid-1;
else l=mid;
}
return l;
}
int main(){
ll l,r;
int u,v,w;
scanf("%d%d%d%d%d",&n,&m,&T,&s,&opt);
if(opt==1)scanf("%lld",&Mod);
for(int i=1;i<=n;i++)scanf("%d",&color[i]);
for(int i=1;i<=m;i++)scanf("%d%d%d",&u,&v,&w),k[u].push_back({v,w}),k[v].push_back({u,w});
Dijkstra();
while(T--){
scanf("%lld%lld",&l,&r);
if(opt==1)l=(l^lastans)%Mod+1,r=(r^lastans)%Mod+1;
if(l>r)swap(l,r);
lastans=0;
for(int i=l;i<=r;i++){
lastans+=Shit(i);
}
printf("%d\n",lastans);
}
return 0;
}
发现只有最后一个点会 T
然后讨一个类似数论分块的就 A 了
#include<bits/stdc++.h>
#ifdef ONLINE_JUDGE
#define getchar getchar_unlocked
#endif
using namespace std;
//Basic Data Part
const int N=500098;
typedef long long ll;
int n,m,T,s,opt,color[N];
ll lastans=0,Mod;
//Line Part
struct node{
int i,d;
bool operator <(const node &rhs)const{
return d>rhs.d;
}
}top;
vector<node> k[N];
//Dijkstra Part
priority_queue<node> q;
int Distance[N],Bdist[N];
bool st[N];
void Dijkstra(){
for(int i=1;i<=n;i++)Distance[i]=INT_MAX;
for(int i=1;i<=666;i++)Bdist[i]=INT_MAX;
q.push({s,0}),Distance[s]=0;
while(!q.empty()){
top=q.top(),q.pop();
if(st[top.i])continue;
st[top.i]=1;
for(node it:k[top.i]){
if(Distance[it.i]>max(Distance[top.i],it.d)){
Distance[it.i]=max(Distance[top.i],it.d);
q.push({it.i,Distance[it.i]});
}
}
}
for(int i=1;i<=n;i++)Bdist[color[i]]=min(Bdist[color[i]],Distance[i]);
sort(Bdist+1,Bdist+601);
}
ll Shit(ll d){
ll l=1,r=600,mid;
while(l<r){
mid=(l+r+1)>>1;
if(d<Bdist[mid])r=mid-1;
else l=mid;
}
return l;
}
inline int Scan(){
char k=getchar();
int ans=0;
while(!isdigit(k))k=getchar();
while(isdigit(k))ans=ans*10+k-'0',k=getchar();
return ans;
}
int main(){
ll l,r;
int u,v,w;
n=Scan(),m=Scan(),T=Scan(),s=Scan(),opt=Scan();
if(opt==1)Mod=Scan();
for(int i=1;i<=n;i++)color[i]=Scan();
for(int i=1;i<=m;i++)u=Scan(),v=Scan(),w=Scan(),k[u].push_back({v,w}),k[v].push_back({u,w});
Dijkstra();
while(T--){
l=Scan(),r=Scan();
if(opt==1)l=(l^lastans)%Mod+1,r=(r^lastans)%Mod+1;
if(l>r)swap(l,r);
lastans=0;
for(ll i=l;i<=r;i++){
ll fuck=Shit(i);
lastans+=fuck*(min(r,(ll)Bdist[fuck+1]-1)-i+1);
i=min(r,(ll)Bdist[fuck+1]-1);
}
printf("%lld\n",lastans);
}
return 0;
}
有一百力!
还有 5min
T3 baoli
#include<bits/extc++.h>
using namespace std;
typedef long long ll;
const ll Mod=1e9+7;
const int N=100098;
int a[N];
ll ans=1;
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<1<<n;i++){
int big=INT_MIN,small=INT_MAX;
for(int j=1;j<=n;j++)if((i>>(j-1))&1)big=max(big,a[j]),small=min(small,a[j]);
ans=(ans*big*small)%Mod;
}
printf("%lld\n",ans);
return 0;
}
170!
赛后部分
题面:Link
总结
我纯唐氏
\(T3\) 成功因为取模原因而挂掉 \(30pts\)
引用我们老师的名言
这场模拟赛浪费了我人生中宝贵的十分钟看完题面。
这场并非全为数学
貌似只有 T3 是
TREE
这道题有一些难度
他其实是一个二进制拆位+简单组合数学
赛时其实想到了一半
但却没有想到二进制拆位
这其实是一个异或问题小 \(trick\)
因为抑或又称不进位加法
所以二进制位之间相互不影响可独立出来看
然后我们对每一个部分单独出来看
我们把快乐 \(val\) 当成一个 \(31\) 位整数
然后对于每个二进制位进行统计答案
假设我们现在枚举到了第 \(i\) 位
则我们这一位对答案的贡献为
算完了这一个结点的贡献在乘上 \(2^{n-size}\) 即可(注意这里的 \(size\) 并非是子树,而是该节点和其直接儿子)
还需要注意特判一下没有节点和一个节点的情况
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100009;
const ll Mod=1e9+7;
ll ans,two[N],fact[N];
int n,val[N],fa[N];
vector<int> k[N];
inline ll Qpow(ll u,ll d){
ll base=u,tmp=1;
while(d){
if(d&1)tmp=(tmp*base)%Mod;
base=(base*base)%Mod,d>>=1;
}
return tmp;
}
inline ll C(ll a,ll b){return fact[b]*Qpow(fact[a],Mod-2)%Mod*Qpow(fact[b-a],Mod-2)%Mod;}
inline int cmp(int u){return (u==1)?0:1;}
int main(){
ll cnt,shit;
scanf("%d",&n),two[0]=fact[0]=1;
for(int i=1;i<=n;i++)scanf("%d",&val[i]),two[i]=two[i-1]*2%Mod,fact[i]=fact[i-1]*i%Mod;
for(int i=2;i<=n;i++)scanf("%d",&fa[i]),k[fa[i]].push_back(i);
for(int i=1;i<=n;i++){
cnt=0;
if(k[i].size()==0)continue;
for(int it=0,tmp;it<=30;it++){//枚举 bit
shit=0;
//统计二进制位中 1 的数量
tmp=0;
for(int u:k[i])if((val[u]>>it)&1)tmp++;
//统计方案
for(int j=1-((val[i]>>it)&1);j<=tmp;j+=2)shit=(shit+C(j,tmp)*two[k[i].size()-tmp])%Mod;
if(i!=1){
//统计去掉根后的方案
for(int j=1;j<=tmp;j+=2)shit=(shit+C(j,tmp)*two[k[i].size()-tmp])%Mod;
}
//计算权值
shit=shit*two[it]%Mod;
cnt=(cnt+shit)%Mod;
}
//去掉没有根只有一个的情况
if(i!=1) for(int u:k[i]) cnt=(cnt-val[u]+Mod)%Mod;
//去掉 Only root
cnt=(cnt-val[i]+Mod)%Mod;
cnt=cnt*two[n-1-k[i].size()-cmp(i)]%Mod;
ans=(ans+cnt)%Mod;
}
printf("%lld",ans);
return 0;
}
TRAVEL
最短路乱搞场切
#include<bits/stdc++.h>
#ifdef ONLINE_JUDGE
#define getchar getchar_unlocked
#endif
using namespace std;
//Basic Data Part
const int N=500098;
typedef long long ll;
int n,m,T,s,opt,color[N];
ll lastans=0,Mod;
//Line Part
struct node{
int i,d;
bool operator <(const node &rhs)const{
return d>rhs.d;
}
}top;
vector<node> k[N];
//Dijkstra Part
priority_queue<node> q;
int Distance[N],Bdist[N];
bool st[N];
void Dijkstra(){
for(int i=1;i<=n;i++)Distance[i]=INT_MAX;
for(int i=1;i<=666;i++)Bdist[i]=INT_MAX;
q.push({s,0}),Distance[s]=0;
while(!q.empty()){
top=q.top(),q.pop();
if(st[top.i])continue;
st[top.i]=1;
for(node it:k[top.i]){
if(Distance[it.i]>max(Distance[top.i],it.d)){
Distance[it.i]=max(Distance[top.i],it.d);
q.push({it.i,Distance[it.i]});
}
}
}
for(int i=1;i<=n;i++)Bdist[color[i]]=min(Bdist[color[i]],Distance[i]);
sort(Bdist+1,Bdist+601);
}
ll Shit(ll d){
ll l=1,r=600,mid;
while(l<r){
mid=(l+r+1)>>1;
if(d<Bdist[mid])r=mid-1;
else l=mid;
}
return l;
}
inline int Scan(){
char k=getchar();
int ans=0;
while(!isdigit(k))k=getchar();
while(isdigit(k))ans=ans*10+k-'0',k=getchar();
return ans;
}
int main(){
ll l,r;
int u,v,w;
n=Scan(),m=Scan(),T=Scan(),s=Scan(),opt=Scan();
if(opt==1)Mod=Scan();
for(int i=1;i<=n;i++)color[i]=Scan();
for(int i=1;i<=m;i++)u=Scan(),v=Scan(),w=Scan(),k[u].push_back({v,w}),k[v].push_back({u,w});
Dijkstra();
while(T--){
l=Scan(),r=Scan();
if(opt==1)l=(l^lastans)%Mod+1,r=(r^lastans)%Mod+1;
if(l>r)swap(l,r);
lastans=0;
for(ll i=l;i<=r;i++){
ll fuck=Shit(i);
lastans+=fuck*(min(r,(ll)Bdist[fuck+1]-1)-i+1);
i=min(r,(ll)Bdist[fuck+1]-1);
}
printf("%lld\n",lastans);
}
return 0;
}
SEQUENCE
这题太糖了
纯签到题
可以排序
然后基本思路就是统计他可以当多少次最大值
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200009;
const ll Mod=1e9+7;
int n,k[N];
ll ans=1;
ll qp(ll d,ll x,ll m){
ll base=d,tmp=1;
while(x){
if(x&1)tmp=(tmp*base)%m;
base=(base*base)%m,x>>=1;
}
return tmp;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&k[i]);
sort(k+1,k+n+1);
for(int i=1;i<=n;i++){
ans=ans*qp(k[i],qp(2,n-i,Mod-1),Mod)%Mod*qp(k[i],qp(2,i-1,Mod-1),Mod)%Mod;
}
printf("%lld\n",ans);
return 0;
}
DOMINO
建树+树形 DP
每个节点的爹就是他左边第一个能干倒他的

浙公网安备 33010602011771号