NOIP模拟89(多校22)

T1 谜之阶乘

解题思路

二分答案,发现 \(a-b\) 至多为 19,毕竟 \(20!\) 已经大于 \(10^{18}\) 了。

对于每一种可能的差值,每一次二分 \(b+1\) 直接枚举乘积进行 check。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int T,n,fac[20];
inline int work(int x,int cnt)
{
	__int128 temp=1;
	for(int i=1;i<=cnt;i++)
	{
		temp*=x+i-1;
		if(temp>n) return 1;
	}
	if(temp==n) return 0;return -1;
}
void solve()
{
	n=read(); if(n==1) return printf("-1\n"),void();
	vector<pair<int,int> > ans;
	for(int cnt=1;cnt<=19&&fac[cnt]<=n;cnt++)
	{
		int l=2,r=n-cnt+1,temp=-1;
		while(l<=r)
		{
			int mid=(l+r)>>1,t=work(mid,cnt);
			if(!t){temp=mid;break;}
			if(t==-1) l=mid+1; else r=mid-1; 
		}
		if(~temp) ans.push_back(make_pair(temp+cnt-1,temp-1));
	}
	sort(ans.begin(),ans.end()); printf("%lld\n",(int)ans.size());
	for(auto it :ans) printf("%lld %lld\n",it.first,it.second);
}
#undef int
int main()
{
	#define int long long
	freopen("factorial.in","r",stdin); freopen("factorial.out","w",stdout);
	T=read(); fac[0]=1;
	for(int i=1;i<=19;i++) fac[i]=fac[i-1]*i;
	while(T--) solve();
	return 0;
}

T2 子集

解题思路

假设每个集合的大小是 \(cnt\) 那么对于 \(cnt\) 是偶数的情况是可以直接对于奇偶按照不同的顺序来填就行。

那么对于 \(n\) 是偶数但是 \(cnt\) 是奇数的情况可以证明是不可行的。

然后对于 \(k,cnt\) 都是奇数的情况发现每个集合的前三个如果可以成功的排布,那么剩下的按照偶数的情况处理就好了。

对于第一列直接 \(1,2,3...n\) 顺序排布就好了,第二列先向 \(\frac{k+1}{2}+1\sim k\) 这一段区间从大到小填,然后再从大到小填 \(1\sim\frac{k+1}{2}\) 这一段区间就好了。

对于剩下的一列直接根据前两列的情况补上就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define count __builtin_popcountll
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e6+10;
int T,n,m,cnt;
vector<int> ans[N];
void work1(int bas,int all)
{
	for(int i=1;i<=all;i++)
		if(i&1) for(int j=1;j<=m;j++) ans[j].push_back(++bas);
		else for(int j=m;j>=1;j--) ans[j].push_back(++bas);
}
void work2(int bas=0)
{
	for(int i=1;i<=m;i++) ans[i].push_back(++bas);
	for(int i=m/2+2;i<=m;i++) ans[i].push_back(++bas);
	for(int i=1;i<=m/2+1;i++) ans[i].push_back(++bas);
	for(int i=1;i<=m;i++) ans[i].push_back(3*(3*m+1)/2-ans[i][0]-ans[i][1]);
}
void solve()
{
	n=read(); m=read(); cnt=n/m;
	if(m==1){printf("Yes\n");for(int i=1;i<=n;i++)printf("%lld ",i);putchar('\n');return ;}
	if(n%2==0&&cnt%2) return printf("No\n"),void();
	if(cnt%2&&cnt<3) return printf("No\n"),void();
	for(int i=1;i<=m;i++) vector<int>().swap(ans[i]); printf("Yes\n");
	if(cnt%2==0) work1(0,cnt);
	else work2(),work1(3*m,cnt-3);
	for(int i=1;i<=m;i++)
	{
		for(auto it:ans[i]) printf("%lld ",it);
		putchar('\n');
	}
}
#undef int
int main()
{
	#define int long long
	freopen("subset.in","r",stdin); freopen("subset.out","w",stdout);
	T=read(); while(T--) solve();
	return 0;
}

T3 混凝土粉末

解题思路

询问本质是:作若干次区间加,并询问 \(x\) 位置上的值最早 \(\ge y\) 的时间。

于是我们可以选择主席树,然后暴力二分+主席树可以达到 \(O(qlog^2n)\) 的复杂度,然后就会被卡常

考虑一种离线做法,我们对于每一次插入操作 \((l,r,h)\) 分别在 \(l,r+1\) 两个地方记录下一个二元组 \((id,h)\)

然后扫描线扫 \(1\sim n\) 这个序列,用建立在操作标号上对于高度进行维护的树状数组或者线段树来处理答案。

这样我们就有了对于每一个下标而言所有有影响的操作的相对时间关系以及对应高度。

每次二分查找一个答案下标 \(mid\) 用树状数组查找 \(mid\) 操作以及以前操作所达到的高度 check。

这就是二分树状数组,由于常数优秀因此可以卡过,线段树或者树状数组二分也是类似的思路。

code

超大常数主席树

#include<bits/stdc++.h>
#define ll long long
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e6+10;
int n,q,all,rt,root[N],id[N]; ll cnt;
struct Segment_Tree
{
	struct Node{int l,r;ll dat;}tre[N*35];
	inline int insert(register int pre,register int l,register int r,register int L,register int R,register int val)
	{
		register int x=++all,mid=(l+r)>>1; tre[x]=tre[pre];
		if(L<=l&&r<=R) return tre[x].dat+=val,x;
		if(L<=mid) ls=insert(tre[pre].l,l,mid,L,R,val);
		if(R>mid) rs=insert(tre[pre].r,mid+1,r,L,R,val);
		return x;
	}
	inline void query(register int x,register int l,register int r,register int pos)
	{
		if(!x||l==r) return cnt+=tre[x].dat,void(); register int mid=(l+r)>>1;
		if(pos<=mid) return cnt+=tre[x].dat,query(ls,l,mid,pos),void();
		return cnt+=tre[x].dat,query(rs,mid+1,r,pos),void();
	}
}T;
int main()
{
	freopen("concrete.in","r",stdin); freopen("concrete.out","w",stdout);
	n=read(); q=read();
	for(register int i=1,opt,x,z;i<=q;i++)
	{
		ll y; opt=read(); x=read(); y=read(); cnt=0;
		if(opt==1){z=read();id[++rt]=i;root[rt]=T.insert(root[rt-1],1,n,x,y,z);continue;}
		T.query(root[rt],1,n,x); if(cnt<y){printf("0\n");continue;}
		register int l=0,r=rt,ans=0;
		while(l<=r)
		{
			register int mid=(l+r)>>1; cnt=0; T.query(root[mid],1,n,x);
			if(cnt>=y) ans=id[mid],r=mid-1; else l=mid+1;
		}
		printf("%d\n",ans);
	}
	return 0;
}

二分树状数组

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e6+10;
int n,q,cnt,ans[N];
vector< pair<int,int> > v[N],ask[N];
struct BIT
{
	int lim,tre[N];
	#define lowbit(x) (x&(-x))
	inline void insert(int x,int val){for(int i=x;i<=lim;i+=lowbit(i))tre[i]+=val;}
	inline int query(int x){int sum=0;for(int i=x;i;i-=lowbit(i))sum+=tre[i];return sum;}
}T;
#undef int
int main()
{
	#define int long long
	freopen("concrete.in","r",stdin); freopen("concrete.out","w",stdout);
	n=read(); q=read(); T.lim=q; memset(ans,-1,sizeof(ans));
	for(int i=1,opt,x,y,z;i<=q;i++)
	{
		opt=read(); x=read(); y=read();
		if(opt==2){ask[x].push_back(make_pair(i,y));continue;}
		z=read(); v[x].push_back(make_pair(i,z));
		v[y+1].push_back(make_pair(i,-z));
	}
	for(int i=1;i<=n;i++)
	{
		for(auto it:v[i]) T.insert(it.first,it.second);
		for(auto it:ask[i])
		{
			int l=1,r=it.first; ans[it.first]=0;
			while(l<=r)
			{
				int mid=(l+r)>>1,temp=T.query(mid);
				if(temp>=it.second) ans[it.first]=mid,r=mid-1;
				else l=mid+1;
			}
		}
	}
	for(int i=1;i<=q;i++) if(~ans[i]) printf("%lld\n",ans[i]);
	return 0;
}

T4 排水系统

解题思路

考虑边 \((x,y)\) 堵塞所造成的影响。

其实就是对于 \(x\) 的其他出点的贡献增加了,而对于 \(y\) 的减少了。

相当于我们可以对于 \(x\) 点加上贡献然后在 \(y\) 点减去就好了。

具体实现就是先忽略所有老化的情况,算出每个点应该有污水,然后依次为基础算出每个点的初始期望污水。

最后在跑一遍就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=2e5+10,M=5e5+10,mod=998244353;
int n,m,base,sta[N],r,ed,cnt[N],du[N],f[N],g[N];
int tot,head[N],ver[M],nxt[M],edge[M],fro[M];
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
void add_edge(int x,int y,int val)
{
	ver[++tot]=y; edge[tot]=val;
	fro[tot]=x; du[y]++; cnt[x]++;
	nxt[tot]=head[x]; head[x]=tot;
}
int power(int x,int y,int p=mod)
{
	int temp=1;
	for(;y;y>>=1,x=x*x%p)
		if(y&1) temp=temp*x%p;
	return temp;
}
#undef int
int main()
{
	#define int long long
	freopen("water.in","r",stdin); freopen("water.out","w",stdout);
	n=read(); m=read(); r=read(); ed=read();
	for(int i=1,x,y,val;i<=ed;i++)
	{
		x=read(); y=read(); val=read();
		add_edge(x,y,val); add(base,val);
	}
	int top=0,pos=0; base=power(base,mod-2);
	for(int i=1;i<=m;i++) sta[++top]=i;
	while(top<n)
	{
		int x=sta[++pos];
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i]; du[to]--;
			if(!du[to]) sta[++top]=to;
		}
	}
	for(int i=1;i<=m;i++) f[i]=1;
	for(int i=1;i<=n;i++)
	{
		int x=sta[i],temp=power(cnt[x],mod-2)*f[x]%mod;
		for(int j=head[x];j;j=nxt[j]) add(f[ver[j]],temp);
	}
	for(int i=1;i<=n;i++)
	{
		if(!cnt[i]) continue;
		int temp=f[i]*power(cnt[i]-1,mod-2)%mod;
		for(int j=head[i];j;j=nxt[j])
		{
			add(g[i],temp*edge[j]%mod*base%mod);
			add(g[ver[j]],(mod-temp%mod)*edge[j]%mod*base%mod);
		}
	}
	for(int i=1;i<=m;i++) add(g[i],1);
	for(int i=1;i<=n;i++)
	{
		int x=sta[i],temp=power(cnt[x],mod-2)*g[x]%mod;
		for(int j=head[x];j;j=nxt[j]) add(g[ver[j]],temp);
	}
	for(int i=n-r+1;i<=n;i++) printf("%lld ",g[i]);
	return 0;
}
posted @ 2021-11-04 17:56  Varuxn  阅读(271)  评论(1编辑  收藏  举报