1. 程式人生 > 其它 >Noip模擬89 2021.11.4

Noip模擬89 2021.11.4

T1 迷之階乘 T2 子集 T3 混凝土粉末 T4 排水系統

預計得分\(100+100+50+68\)

實際得分\(100+35+30+44\)

不知道說啥好。。。。非常可惜

\(TLEcoders\)的測評姬卡了大概\(20\),不可預估的\(MLE\)卡掉了\(20\)

剩下的。。。。。

\(T2\)沒特判\(n=1,k=1\)掛掉了\(65\)分,只對了\(T=1\)的點。。。。

但凡\(T!=1\)的點第一個都是\(n=1,k=1\)什麼鬼啊!!!

可以看出幹啥事都要細心,不要因為自己已經非常接近正解就不去思考細處

否則就會有不可估量的錯誤讓你後悔莫及。。。(別扯淡了,醒醒,喂喂)

T1 迷之階乘

比較水,考場上應該可以直接幹掉

發現他在找下降冪是否合法,那麼我們直接列舉下降冪次數是\(log\)

級別的

然後找到\(n^{\frac{1}{i}}\)表示可能然後下降冪次數\(\underline{i}\)合法的第一個數,一直試到\(x^{\underline{i}}\geq n\)即可

factorial
#include<bits/stdc++.h>
#define int long long
using namespace std;
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 T,n,ans;
struct answer{int x,y;}p[65];
namespace WSN{
	inline short main(){
		FILE *wsn=freopen("factorial.in","r",stdin);
		wsn=freopen("factorial.out","w",stdout);
		T=read();
		while(T--){
			n=read(); ans=0;
			if(n==1){puts("-1");continue;}
			p[++ans]=answer{n,n-1};
			for(int i=2;i<64;++i){
				int st=pow(1.0*n,1.0/i),res;
				while(1){
					res=1;
					for(int j=1;j<=i;++j)
						res=res*(st-j+1);
					if(res>n||res<0) break;
					if(res==n){
						if(st-i>0)
							p[++ans]=answer{st,st-i};
						break;
					}
					++st;
				}
			}
			write(ans);
			for(int i=ans;i;--i){
				write(p[i].x,' ');
				write(p[i].y);
			}
		}
		return 0;
	}
}
signed main(){return WSN::main();}

T2 子集

比較水的構造,還是非正解想的時間比正解長

\(\frac{n}{k}=m\),把輸出答案看成矩陣,那麼這是一個\(k\times m\)的矩陣,要求構造這個矩陣保證每一行的數加和相等。

首先特判非法解

\(\huge{n=1,k=1輸出Yes!!!}\)

然後剩下的\(n=1\)輸出\(No\),然後在奇數列偶數行的時候輸出\(No\)

證明考慮等差數列,下面會說

先考慮\(m\)(即列數)是偶數的時候

因為\(n\)個元素構成了長度為\(1\)的等差數列,那麼不難想到我們可以以每一列為單位,一條龍式的給這個數列疊起來

證明也很簡單,因為這樣保證了每兩列的大小關係是正好相反的(即一大一小)

那麼我們可以接著按照這種思路去想奇數列的時候

不難發現我們只構造前三列,保證前三列每一行的和是相等的,剩下的\(m-3\)行直接按照偶數的構造即可

那麼為什麼偶數行的時候不行呢?

我們設\(S=\frac{(1+3k)\times 3k}{2}\)表示前\(3k\)和數的和

那麼如果有方案,則有\(k|S\),而\(S/k=\frac{(1+3k)\times 3}{2}\),如果\(k\)是偶數,分子一定是奇數,發現\(S\)不是\(k\)的倍數,則無解

好了,無解的都完了。。。

那麼考慮奇數如何造,第一、二、三列還是\(1,2,3,4,5\),\(6,7,8,9,10\),\(11,12,13,14,15\)

我們為了保證最後一列的\(11,12,13,14,15\)放完之後和相等,就要保證前兩列湊出來的數的和排完序後正好是一個長度為\(k\)的等差數列

這樣之後我們就可以直接按照大小關係分配第三列的數了,即大的配小的,小的找大的(沒別的意思)

所以我們現在的問題就是構造前兩列,使得這兩列每一行的兩個數相加得到的數排完序是一個公差為一等差數列,

不難發現可以這樣構造

定義\(mid=(1+k)>>1\)

發現這樣構造出來的行之和在\(mid\)上面全部是奇數,下面全部是偶數,\(mid\)行數最大

這樣我們就解決了奇數列的問題

比題解好構造一萬倍,思路也是最簡單的,程式碼跑著也是最快的

subset
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e6+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 T,n,k,m,ans[NN];
auto id=[](int x,int y){return (x-1)*m+y;};
auto task=[](){
	int tmp=0; 
	for(int i=1;i<=m;i++){
		if(i&1) for(int j=1;j<=k;j++) ans[id(j,i)]=++tmp;
		else for(int j=k;j;j--) ans[id(j,i)]=++tmp;
	}
	puts("Yes");
	for(int i=1;i<=k;i++)
		for(int j=1;j<=m;j++)
			write(ans[id(i,j)],j==m?'\n':' ');
	return;
};
auto solve=[](){
	n=read();k=read();m=n/k;
	if(n==1&&k==1){
		printf("Yes\n1\n");
		return;
	}
	if(m==1) return puts("No"),void();
	if(!(m&1)) return task(),void();
	if(!(k&1)) return puts("No"),void();
	int tmp=0,mid=((1+k)>>1);
	for(int i=1;i<=k;i++) ans[id(i,1)]=++tmp;
	for(int i=mid+1;i<=k;i++) ans[id(i,2)]=++tmp;
	for(int i=1;i<=mid;i++) ans[id(i,2)]=++tmp;
	ans[id(mid,3)]=++tmp; int tot=0;
	for(int i=1;i<=3;i++) tot+=ans[id(mid,i)];
	for(int i=1;i<=k;i++)if(i!=mid)ans[id(i,3)]=tot-ans[id(i,2)]-ans[id(i,1)];
	tmp=3*k;
	for(int i=4;i<=m;i++){
		if(i&1) for(int j=1;j<=k;j++) ans[id(j,i)]=++tmp;
		else for(int j=k;j;j--) ans[id(j,i)]=++tmp;
	}
	puts("Yes");
	for(int i=1;i<=k;i++)
		for(int j=1;j<=m;j++)
			write(ans[id(i,j)],j==m?'\n':' ');
	return;
};
namespace WSN{
	inline short main(){
		FILE *wsn=freopen("subset.in","r",stdin);
		wsn=freopen("subset.out","w",stdout);
		T=read();
		while(T--)solve();
		return 0;
	}
}
signed main(){return WSN::main();}

T3 混凝土粉末

考場打了一個珂朵莉樹,這東西騙分真方便,不過\(MLE\)了,就比較恐怖

正解線段樹加掃描線

把詢問離線,維護一個時間軸一個空間軸,

空間軸用來差分,即在一個詢問的\(l,r\)處分別做加減

時間軸用來線段樹二分,找到最早的\(\geq y\)\(col\)

concrete
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=1e6+5;
namespace AE86{
	FILE *wsn;
	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,q,ans[NN];
struct SNOWtree{
	#define lid (id<<1)
	#define rid (id<<1|1)
	#define mid ((l+r)>>1)
	int sm[NN<<2],res,mx;
	inline void pushup(int id,int l,int r){if(l==r) return;sm[id]=sm[lid]+sm[rid];}
	inline void insert(int id,int l,int r,int pos,int v){
		if(l==r) return sm[id]+=v,void();
		if(pos<=mid) insert(lid,l,mid,pos,v);
		else insert(rid,mid+1,r,pos,v);
		pushup(id,l,r);
	}
	inline void query(int id,int l,int r,int pos){
		if(res>=0||l>pos) return;
		if(r<=pos){
			if(l==r){
				if(sm[id]>=mx) res=l;
				else mx-=sm[id];
				return;
			}
			if(mx>sm[lid]) mx-=sm[lid],query(rid,mid+1,r,pos);
			else query(lid,l,mid,pos);
			return;
		}
		query(lid,l,mid,pos);
		query(rid,mid+1,r,pos);
		return;
	}
	#undef mid
	#undef lid
	#undef rid
}tr;
struct node{int hi,tim;};
vector<node> add[NN],del[NN],qu[NN];
namespace WSN{
	inline short main(){
		wsn=freopen("concrete.in","r",stdin);
		wsn=freopen("concrete.out","w",stdout);
		n=read();q=read();memset(ans,-1,sizeof(ans));
		int opt,l,r,h,x,y,col;
		for(int i=1;i<=q;++i){
			opt=read();
			if(opt==1){
				l=read();r=read();h=read();
				add[l].push_back(node{h,i});
				del[r+1].push_back(node{h,i});
			}
			if(opt==2){
				x=read();y=read();
				qu[x].push_back(node{y,i});
			}
		}
		for(int i=1;i<=n;i++){
			for(auto j:add[i]) tr.insert(1,1,q,j.tim,j.hi);
			for(auto j:del[i]) tr.insert(1,1,q,j.tim,-j.hi);
			for(auto j:qu[i]){
				tr.res=-1; tr.mx=j.hi;
				tr.query(1,1,q,j.tim);
				ans[j.tim]=(tr.res==-1?0:tr.res);
			}
		}
		for(int i=1;i<=q;i++) if(ans[i]!=-1) write(ans[i]);
		return 0;
	}
}
signed main(){return WSN::main();}

T4 排水系統

思路差不多和那個一樣,不過就是要用兩遍拓撲排序,第二遍要找哪些位置可能有出入

題解說的比較清楚,直接看就好了

water
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353,NN=5e5+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);
	};
	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);};
}using namespace AE86;

int n,m,r,k,in[NN],use[NN],out[NN],a[NN],cnt;
int pr[NN],ans[NN];

struct edge{int u,v;}p[NN];
struct SNOW{int fr,to,next;}e[NN];int head[NN],rp;
auto add=[](int x,int y){e[++rp]=SNOW{x,y,head[x]};head[x]=rp;};

queue<int> q;

auto topo=[](){
	for(int i=1;i<=m;i++)q.push(i),ans[i]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		int tmp=ans[x]*inv(out[x])%mod;
		for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to){
			ans[y]=(ans[y]+tmp)%mod;
			--in[y]; if(!in[y]) q.push(y);
		}
	}
};

auto retopo=[](){
	memcpy(in,use,sizeof(use));
	for(int i=1;i<=m;i++)q.push(i),pr[i]=1;
	for(int i=1;i<=k;i++){
		int fr=e[i].fr,to=e[i].to,P=a[i]*cnt%mod,tmp,res;
		tmp=ans[fr]*inv(out[fr])%mod*P%mod;
		res=ans[fr]*inv(out[fr]-1)%mod*P%mod;
		pr[to]=(pr[to]-res+mod)%mod;
		pr[fr]=(pr[fr]+out[fr]*(res-tmp+mod)%mod)%mod;
	}
	while(!q.empty()){
		int x=q.front();q.pop(); int tmp=inv(out[x]);
		for(int i=head[x],y=e[i].to;i;i=e[i].next,y=e[i].to){
			--in[y]; if(!in[y]) q.push(y);
			pr[y]=(pr[y]+pr[x]*tmp%mod)%mod;
		}
	}
};

namespace WSN{
	inline short main(){
		FILE *wsn=freopen("water.in","r",stdin);
		wsn=freopen("water.out","w",stdout);
		n=read();m=read();r=read();k=read();
		for(int i=1;i<=k;++i){
			int u=read(),v=read();a[i]=read();
			cnt=(cnt+a[i])%mod;
			add(u,v); ++in[v]; ++out[u];
		}
		cnt=inv(cnt);memcpy(use,in,sizeof(in));
		topo(); retopo();
		for(int i=n-r+1;i<=n;++i)write(pr[i],' ');puts("");
		return 0;
	}
}
signed main(){return WSN::main();}