2021.11.4考试总结[冲刺NOIP模拟22]

继续被踩...

主要是T2做上头了,只给后两题留了1h,而且还没构造出3的方案。。时间分配有很大问题

T1 迷之阶乘

\(n\) 拆为 \(i\) 个连续自然数的和,那么这 \(i\) 个数中一定有 \(\left\lfloor\sqrt[i]n\right\rfloor\)\(\left\lceil\sqrt[i]n\right\rceil\)。然后直接爆扫即可。

直接用 pow 开根会爆精度,用 long double 就没事了。

\(code:\)

T1
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	typedef long long LL;
	typedef double DB;
	typedef long double LD;
	int read(){
		LL 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;
	}
	void write(LL x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;

int t,n,lmt;
vector<pair<int,int>>ans;

signed main(){
	freopen("factorial.in","r",stdin);
	freopen("factorial.out","w",stdout);
	t=read();
	while(t--){
		n=read(); ans.clear();
		if(n==1){ puts("-1"); continue; }
		for(int i=21;i;i--){
			int res=1,tmp=pow((LD)n,(LD)1.0/i);
			int l=max(2ll,tmp-i+1),r=l+i-1;
			for(int j=l;j<=r;j++) res*=j;
			if(res==n){ ans.push_back(make_pair(l-1,r)); continue; }
			while(l<tmp&&res<n){
				res/=l; res*=(r+1);
				++l; ++r;
				if(res==n){ ans.push_back(make_pair(l-1,r)); break; }
				if(res<0) break;
			}
		}
		write(ans.size(),'\n');
		for(auto i:ans) write(i.second,' '),write(i.first,'\n');
	}
	return 0;
}

T2 子集

特判 \(n=1\wedge k=1\) 的情况。令 \(m=\frac{n}{k}\) ,那么当 \(m=1\) 时显然无解,当 \(n\) 为偶数, \(m\) 为奇数时, \(k\) 不整除 \(\sum_{i=1}^ni\) ,也无解。

接下来,只要能构造出向所有集合加入 \(2\)\(3\) 个元素,是它们和相等的方案,即可构造出解。

加入两个很简单,一条龙加入即可。加入三个时,第一列依次加入,第二列从中间下方开始加入,第三列按差值补上即可。

\(k=5\) 为例:

\[\begin{bmatrix}1&10\\2&9\\3&8\\4&7\\5&6\end{bmatrix} \begin{bmatrix}1&8&15\\2&9&13\\3&10&11\\4&6&14\\5&7&12\end{bmatrix} \]

\(code:\)

T2
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	typedef long long LL;
	int read(){
		LL 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;
	}
	void write(LL x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;

int t,n,m,k;
vector<int>ans[1000010];

void solve_even(int ed){
	int tmp;
	for(int i=1;i<=k;i++)
		for(int j=1;j<=ed;j++){
			tmp=(j-1)*k+((j&1)?i:k-i+1);
			ans[i].push_back(tmp);
		}
}
void solve_odd(int st){
	int sum=(st+1+n)*(n-st)/2/k;
	for(int i=1;i<=k;i++) ans[i].push_back(++st);
	for(int i=(k+1)/2+1;i<=k;i++) ans[i].push_back(++st);
	for(int i=1;i<=(k+1)/2;i++) ans[i].push_back(++st);
	for(int i=1;i<=k;i++) ans[i].push_back(sum-ans[i][m-2]-ans[i][m-3]);
}
void print(){
	for(int i=1;i<=k;i++){
		for(int v:ans[i]) write(v,' ');
		puts("");
	}
	for(int i=1;i<=k;i++) ans[i].clear();
}

signed main(){
	freopen("subset.in","r",stdin);
	freopen("subset.out","w",stdout);
	t=read();
	while(t--){
		n=read(); k=read(); m=n/k;
		if(n==1&&k==1){ puts("Yes"); puts("1"); continue;}
		if(m==1){ puts("No"); continue; }
		if((m&1)&&!(n&1)){ puts("No"); continue; }
		puts("Yes");
		if(k==1){ for(int i=1;i<=n;i++) write(i,' '); puts(""); continue; }
		if(!(m&1)) solve_even(m);
		else solve_even(m-3),solve_odd((m-3)*k);
		print();
	}
	return 0;
}

T3 混凝土粉末

转化题意,实际上需要支持区间加与查询单点值达到某值的最早时刻。

题面中有位置与时间两层限制,考虑通过离线去掉一维。区间限制起点终点不确定,时间限制却都是从 \(0\) 开始,于是可以对时间离线操作。区间方面,扫描线可以很轻松地解决。

开以时间为下标的线段树,记录当前扫到的位置在每一时刻的大小,每次查询线段树二分即可。

\(code:\)

T3
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	typedef long long LL;
	int read(){
		LL 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;
	}
	void write(LL x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;

const int NN=1000010;
int n,Q,tot,ans[NN];
vector<pair<int,int>>add[NN],qry[NN];

namespace Segment_tree{
	#define ld rt<<1
	#define rd (rt<<1)|1
	int mx,res,sum[NN<<2];
	void pushup(int rt){ sum[rt]=sum[ld]+sum[rd]; }
	void insert(int rt,int l,int r,int pos,int val){
		if(l==r) return sum[rt]+=val,void();
		int mid=l+r>>1;
		if(pos<=mid) insert(ld,l,mid,pos,val);
		else insert(rd,mid+1,r,pos,val);
		pushup(rt);
	}
	void Search(int rt,int l,int r,int ext){
		if(~res||l>ext) return;
		if(r<=ext){
			if(l==r) sum[rt]>=mx?res=l:mx-=sum[rt];
			else{
				int mid=l+r>>1;
				if(sum[ld]>=mx) Search(ld,l,mid,ext);
				else mx-=sum[ld],Search(rd,mid+1,r,ext);
			}
			return;
		}
		int mid=l+r>>1;
		Search(ld,l,mid,ext);
		Search(rd,mid+1,r,ext);
	}
} using namespace Segment_tree;

signed main(){
	freopen("concrete.in","r",stdin);
	freopen("concrete.out","w",stdout);
	n=read(); Q=read();
	memset(ans,-1,sizeof(ans));
	for(int i=1;i<=Q;i++){
		int op=read();
		if(op==1){
			int x=read(),y=read(),h=read();
			add[x].push_back(make_pair(h,i));
			add[y+1].push_back(make_pair(-h,i));
		} else{
			int x=read(),y=read();
			qry[x].push_back(make_pair(y,i));
		}
	}
	for(int i=1;i<=n;i++){
		for(auto v:add[i]) insert(1,1,Q,v.second,v.first);
		for(auto q:qry[i]){
			res=-1; mx=q.first;
			Search(1,1,Q,q.second);
			ans[q.second]=max(res,0ll);
		}
	}
	for(int i=1;i<=Q;i++) if(~ans[i]) write(ans[i],'\n');
	return 0;
}

T4 排水系统

不删边的答案直接拓扑排序。

考虑删去一条边 \((u\to v)\) 之后对图的变化,可以看作 \(v\) 点的值减少, \(u\) 点的其它出点值增加。而这些值增加或减少 \(x\) ,等价于这些位置在开始时的值为 \(x\)\(-x\)

而由于 \(u\) 的出点值的增加量是均匀的,因此它们值的变化又能转化为 \(u,v\) 值的变化。因此处理出初始时所有点的期望值,再拓扑一遍就能求出答案。

\(code:\)

T4
#include<bits/stdc++.h>
#define int long long
using namespace std;

namespace IO{
	typedef long long LL;
	int read(){
		LL 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;
	}
	void write(LL x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(LL& x,LL y){ x=x>y?x:y; }
} using namespace IO;

const int NN=200010,MM=500010,mod=998244353;
int n,m,r,k,sum,p[MM],ans[NN],in[NN],out[NN],deg[NN];
int idx,st[MM],to[MM],nex[MM],head[NN];
int fin[NN];
int inl[1010][1010];
void add(int a,int b,int c){ to[++idx]=b; st[idx]=a; nex[idx]=head[a]; head[a]=idx; p[idx]=c; ++deg[b]; ++out[a]; (sum+=c)%=mod; }
int qpow(int a,int b){
	int res=1;
	for(;b;b>>=1){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
	}
	return res;
}

queue<int>q;
void topo(){
	memcpy(in,deg,sizeof(deg));
	for(int i=1;i<=m;i++) ans[i]=1,q.push(i);
	while(!q.empty()){
		int x=q.front(); q.pop();
		for(int i=head[x],v=to[i];i;i=nex[i],v=to[i]){
			--in[v];
			(ans[v]+=ans[x]*qpow(out[x],mod-2))%=mod;
			if(!in[v]) q.push(v);
		}
	}
}
void topo_sort(){
	memcpy(in,deg,sizeof(deg));
	for(int i=1;i<=m;i++) fin[i]=1,q.push(i);
	for(int i=1;i<=idx;i++){
		int tmp=ans[st[i]]*qpow(out[st[i]],mod-2)%mod*p[i]%mod*qpow(sum,mod-2)%mod;
		int pmt=ans[st[i]]*qpow(out[st[i]]-1,mod-2)%mod*p[i]%mod*qpow(sum,mod-2)%mod;
		(fin[to[i]]+=mod+mod-pmt)%=mod;
		(fin[st[i]]+=out[st[i]]*(mod+pmt-tmp)%mod)%=mod;
	}
	while(!q.empty()){
		int x=q.front(); q.pop();
		for(int i=head[x],v=to[i];i;i=nex[i],v=to[i]){
			--in[v];
			(fin[v]+=fin[x]*qpow(out[x],mod-2))%=mod;
			if(!in[v]) q.push(v);
		}
	}
}

signed main(){
	freopen("water.in","r",stdin);
	freopen("water.out","w",stdout);
	n=read(); m=read(); r=read(); k=read();
	for(int a,b,c,i=1;i<=k;i++)
		a=read(),b=read(),c=read(),add(a,b,c);
	topo(); topo_sort();
	for(int i=n-r+1;i<=n;i++) write(fin[i],' ');
	return puts(""),0;
}
posted @ 2021-11-04 17:06  keen_z  阅读(85)  评论(0编辑  收藏  举报