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^i\sum_{i=1}^{tot}{C^i_{tot}[i\%2==1]} \]

算完了这一个结点的贡献在乘上 \(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
每个节点的爹就是他左边第一个能干倒他的

posted @ 2025-03-30 12:56  2025ing  阅读(32)  评论(0)    收藏  举报