Loading

Noip模拟84 2021.10.27

以后估计都是用\(markdown\)来写了,可能风格会有变化

T1 宝藏

这两天老是会的题打不对,还是要细心。。。

考场上打的是维护\(set\)的做法,但是是最后才想出来的,没有维护对于是没有交。。

然后觉得细节太多于是下午来的就去打了比较好搞得权值线段树

基本思想就是用一个指针\(pos\)指向当前需要更新的答案(\(pos\times 2+1\)是题目输入的选择的数的个数,这样解释应该就好懂了)

那么考虑先将所有宝藏按照价值从小到大排序,发现从最后开始向前扫每一个宝藏,他可以更新的\(pos\)是单调递增的

例如,当前考虑下标为\(x\)的宝藏价值为中位数最大值的时候,

他的可以更新的这个\(pos\)值域取在\([pos,\min(n-x,x-1)]\)一段区间,

那么我们只需要看,从\(x\)的左边和右边分别选择\(pos\)个宝藏,他们的\(\sum t\)是否有可能小于\(T\)

显然每次找到最小的前\(pos\)\(t\)\(\sum\)是最优的,所以开两颗权值线段树分别维护当前的\(x\)左边和右边的值即可

treasure
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=3e5+5;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;
int n,T,Q,x,ans[NN],mx,dis[NN];
struct node{
	int w,t;
	bool operator<(const node&a)const{
		return w==a.w?t<a.t:w<a.w;
	}
}p[NN];
struct SNOWtree{
	#define lid (id<<1)
	#define rid (id<<1|1)
	int ll[NN<<2],rr[NN<<2],nm[NN<<2],sm[NN<<2];
	inline void pushup(int id){if(ll[id]==rr[id])return;nm[id]=nm[lid]+nm[rid];sm[id]=sm[lid]+sm[rid];}
	inline void build(int id,int l,int r){ll[id]=l;rr[id]=r;if(l==r)return;int mid=l+r>>1;build(lid,l,mid);build(rid,mid+1,r);}
	inline void insert(int id,int pos,int v){
		if(ll[id]==rr[id])return nm[id]+=v,sm[id]+=v*dis[pos],void();int mid=ll[id]+rr[id]>>1;
		if(pos<=mid)insert(lid,pos,v); else insert(rid,pos,v); pushup(id);
	}
	inline int query(int id,int lim){
		if(!lim) return 0;
		if(nm[id]<=lim) return sm[id];
		if(ll[id]==rr[id]) return dis[ll[id]]*lim;
		int mid=ll[id]+rr[id]>>1;
		if(lim<=nm[lid]) return query(lid,lim);
		return sm[lid]+query(rid,lim-nm[lid]);
	}
}ri,le;
int pos;
auto solve=[](int x){
	int r=min(n-x,x-1); if(pos>r) return;
	while(pos<=r){
		int tot=ri.query(1,pos)+le.query(1,pos)+dis[p[x].t];
		if(tot<=T) ans[pos*2+1]=p[x].w,++pos; else break;
	}
};
namespace WSN{
	inline short main(){
		// freopen("in.in","r",stdin);freopen("bl.out","w",stdout);
		freopen("treasure.in","r",stdin);freopen("treasure.out","w",stdout);
		n=read();T=read();Q=read();
		for(int i=1;i<=n;i++) p[i].w=read(),p[i].t=read(),dis[i]=p[i].t;
		sort(p+1,p+n+1); memset(ans,-1,sizeof(ans));
		sort(dis+1,dis+n+1); mx=unique(dis+1,dis+n+1)-dis-1;
		for(int i=1;i<=n;i++) p[i].t=lower_bound(dis+1,dis+mx+1,p[i].t)-dis;
		ri.build(1,1,mx);le.build(1,1,mx);
		for(int i=1;i<=n;++i) le.insert(1,p[i].t,1);
		for(int i=n;i;--i) le.insert(1,p[i].t,-1),solve(i),ri.insert(1,p[i].t,1);
		while(Q--) x=read(),write(ans[x]);
		return 0;
	}
}
signed main(){return WSN::main();}

T2 寻找道路

对不起,坏孩子没有打正解,直接使用\(bitset\)可以淼过去

考场上打了一半的\(bitset\)然后发现使用\(dijkstra\)无法优化其对于堆优化时候的比较函数

然后当时不知道咋想的,也没有试一试\(spfa\)\(spfa\)只有一次比较,写起来非常方便

只要写了\(spfa+bitset\)就可以切掉了,但是只能说是淼过去的

非常好写,就是注意删除前导\(0\)就可以了

path
#include<bits/stdc++.h>
#define int long long
#define si(i,x) for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to)
using namespace std;
const int NN=2e6+5,mod=1e9+7;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;
int n,m;
struct SNOW{int to,val,next;}e[NN];int head[NN],rp=1;
auto add=[](int x,int y,int z){e[++rp]=SNOW{y,z,head[x]};head[x]=rp;};
int dis[NN],wei[NN];
bitset<1001> bt[NN],tmp;
bool vis[NN];
queue<int>q;
auto spfa=[](){
	for(int i=1;i<=n;i++)
		dis[i]=1e18,wei[i]=1000,bt[i].flip();
	q.push(1); vis[1]=true;
	dis[1]=0; wei[1]=1; bt[1].reset(); bt[1]=0;
	while(!q.empty()){
		int x=q.front(); q.pop(); vis[x]=false;
		si(i,x){
			bool cmp=0; int wi=wei[x]+1;
			if(bt[x][wei[x]]==0) --wi;
			tmp=bt[x]<<1; tmp[1]=e[i].val;
			if(wei[y]>wi) cmp=1;
			else if(wei[y]<wi) cmp=0;
			else{
				for(int j=wei[y];j;--j){
					if(bt[y][j]>tmp[j]) {cmp=1;break;}
					else if(bt[y][j]<tmp[j]) {cmp=0;break;}
				}
			}
			if(cmp){
				bt[y]=tmp; wei[y]=wi;
				dis[y]=(dis[x]*2%mod+e[i].val)%mod;
				if(!vis[y]){
					vis[y]=true;
					q.push(y);
				}
			}
		}
	}
};
namespace WSN{
	inline short main(){
		freopen("path.in","r",stdin);
		freopen("path.out","w",stdout);
		n=read();m=read();
		for(int i=1,u,v,w;i<=m;i++)
			u=read(),v=read(),w=read(),add(u,v,w);
		spfa();
		for(int i=2;i<=n;i++)
			if(dis[i]>=1e18) write(-1,' ');
			else write(dis[i],' ');
		puts("");
		return 0;
	}
}
signed main(){return WSN::main();}

T3 猪国杀

又搞这一套,又想吓我,已经无法被吓倒了

不过题目还是很神仙的假期望,真计数

设方案数为\(ans\),那么答案就是

\(ans=\sum\limits_{i=0}^{n}\sum\limits_{j=1}^{A}\sum\limits_{k=1}^{n-i}g_{i,j-1,m-j*k}\times C_n^i\sum\limits_{t\geq k}C_{n-i}^{t}\times(A-j)^{n-i-t}\)

其中\(g_{i,j,k}\)表示有多少个长度为\(i\)的序列满足每一个数字都不大于\(j\)且所有数字的总和不大于\(k\)

以上式子可以理解为枚举序列的长度,然后枚举最大值\(j\),然后再枚举最大值的个数\(k\),然后剩下的\(n-i\)个可以在去掉最大值的限制后随便选

你可能会问,这个算方案数的式子为什么不用考虑每次的贡献?

实际上他每个合法的序列计算的次数正好为他做的贡献数,即每个合法序列都会计算\(i+k\)

那么考虑容斥出\(g_{i,j,k}=\sum\limits_{t=0}^{i}(-1)^t\times C_i^t\times C_{k-j*t}^{i}\)

那么原式的转化我就不写了,随便代一下就出来了,直接照这个式子抄就可以了,把没有贡献的地方直接跳过不计算

legend
#include<bits/stdc++.h>
#define int long long
#define si(i,x) for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to)
using namespace std;
const int NN=101,MM=3001,mod=998244353;

namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;

int n,m,A,ans;

namespace Math{
	int h[MM],v[MM];
	auto qmo=[](int a,int b,int ans=1){
		int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
		return ans;
	};
	auto inv=[](int x){return qmo(x,mod-2);};
	auto prework=[](){
		h[0]=h[1]=1; v[0]=v[1]=1; for(int i=2;i<MM;++i)h[i]=h[i-1]*i%mod;
		v[MM-1]=inv(h[MM-1]); for(int i=MM-2;i>=2;--i)v[i]=v[i+1]*(i+1)%mod;
	};
	auto C=[](int n,int m){return (n<m||n<0||m<0) ? 0 : (h[n]*v[n-m]%mod*v[m]%mod);};
}using namespace Math;

namespace WSN{
	inline short main(){
		freopen("legend.in","r",stdin);
		freopen("legend.out","w",stdout);
		n=read();m=read();A=read();prework();
		for(int i=0;i<=n;++i){
			for(int j=1;j<=A;++j){
				for(int k=1;k<=n-i;++k){
					if(m-k*j<0)continue;
					int tmp=0,res=0,bs=1;
					for(int t=0;t<=i&&m-k*j-t*(j-1)>=i;++t,bs=-bs)
						tmp=(tmp+bs*C(i,t)%mod*C(m-k*j-t*(j-1),i)%mod+mod)%mod;
					tmp=tmp*C(n,i)%mod;
					for(int t=k;t<=n-i;++t)
						res=(res+C(n-i,t)*qmo(A-j,n-i-t)%mod)%mod;
					ans=(ans+tmp*res%mod)%mod;
				}
			}
		}
		write(ans*inv(qmo(A,n))%mod);
		return 0;
	}
}
signed main(){return WSN::main();}

T4 数树

数据范围提示状态压缩,那么考虑如何设状态

设计状态\(f[x][S]\)表示\(x\)子树中的点集为\(S\)的方案数

例如,\(S=0001110\)就表示\(2,3,4\)这三个点是某一个点的子孙节点

那么因为\(T2\)的形状对于不同的节点做根可能会不一样,所以以每一个节点做根都跑一边\(dp\)

然后注意去重,具体操作就是用\(map\)预处理出会重复算多少次,最后的\(dp\)数组再把算重复的除掉就行了

count
#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int NN=3005,mod=998244353;
namespace AE86{
	auto read=[](){
		int x=0,f=1;char ch=getchar();
		while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
		return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);
	};
}using namespace AE86;
auto qmo=[](int a,int b,int ans=1){
	int c=mod;for(;b;b>>=1,a=a*a%c)if(b&1)ans=ans*a%c;
	return ans;
};auto inv=[](int x){return qmo(x,mod-2);};

int n,m,v[11],S[1<<10],tot,root,T[11],dp[NN][1<<10],tmp[11];
vector<int> son[11];
struct Graph{
	#define star(e,i,x) for(int i=e.head[x],y=e.to[i];i;i=e.nxt[i],y=e.to[i])
	int to[NN<<1],nxt[NN<<1],head[NN],rp;
	inline void add(int x,int y){
		to[++rp]=y; nxt[rp]=head[x]; head[x]=rp;
		to[++rp]=x; nxt[rp]=head[y]; head[y]=rp;
	}
}e,g;
unordered_map<int,bool>mp;
unordered_map<int,int>t[11];
inline void dfs(int f,int x,int bs){
	S[x]|=(1<<x-1);tot+=bs;
	star(g,i,x) if(y!=f){
		son[x].pb(y); dfs(x,y,bs*10);
		++t[x][T[y]];
		S[x]|=S[y];T[x]+=T[y];
	}
	star(g,i,x) if(y!=f) if(t[x].find(T[y])!=t[x].end())
		tmp[x]=tmp[x]*v[t[x][T[y]]]%mod,t[x].erase(T[y]);
	T[x]+=bs;
}
int res[1<<10],ans;
inline void DP(int f,int x){
	for(int i=0;i<m;i++) dp[x][1<<i]=1;
	star(e,i,x) if(y!=f){
		DP(x,y);
		for(int j=1;j<(1<<m);j++) res[j]=dp[x][j];
		for(int j=1;j<(1<<m);j++) if(dp[x][j])
			for(int k=1;k<=m;k++) if(!(j&S[k])&&dp[y][S[k]])
				dp[x][S[k]|j]=(dp[x][S[k]|j]+dp[y][S[k]]*res[j]%mod)%mod;
	}
	for(int i=1;i<=m;i++) dp[x][S[i]]=dp[x][S[i]]*tmp[i]%mod;
	ans=(ans+dp[x][(1<<m)-1])%mod;
}
namespace WSN{
	inline short main(){
		freopen("count.in","r",stdin);freopen("count.out","w",stdout);
		v[0]=1; for(int i=1;i<=10;i++) v[i]=v[i-1]*inv(i)%mod;
		n=read();for(int i=1;i<n;i++)e.add(read(),read());
		m=read();for(int i=1;i<m;i++)g.add(read(),read());
		for(int i=1;i<=m;i++){
			for(int j=1;j<=m;j++)t[j].clear(),son[j].clear(),tmp[j]=1;
			memset(S,0,sizeof(S));
			memset(T,0,sizeof(T));
			memset(dp,0,sizeof(dp));
			tot=0; root=i;
			dfs(0,root,1);
			if(mp.find(tot)==mp.end())
				mp[tot]=true,DP(0,1);
		}
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}
posted @ 2021-10-27 21:18  雪域亡魂  阅读(89)  评论(0编辑  收藏  举报