noip模擬32[好數學啊]

fengwu2005發表於2021-08-07

noip模擬32 solutions

真是無語子,又沒上100,無奈死了

雖然我每次都覺得題很難,但是還是有好多上100的

戰神都200多了,好生氣啊啊啊

從題開始變難之後,我的時間分配越來越不均勻,導致每次都沒有時間做最後一題

今天直接掛掉了30pts,因為最後一題沒有注意部分分。。

T1 smooth

這個最簡單了,我考場上一秒出80pts做法,直接一波set維護

自帶排序和去重,完全不必擔心,就是時間複雜度多了個log

80pts.set
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e5+5;
const ll maxn=1e18;
int b,k;
ll pt[N],tot;
ll p[25]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};
set<ll> st;
int sum;
signed main(){
	scanf("%d%d",&b,&k);
	if(k==1){
		printf("1");
		return 0;
	}
	st.insert(1);
	while(st.size()){
		set<ll>::iterator it=st.begin();
		ll tmp=*it;
		sum++;//cout<<*it<<endl;
		//cout<<sum<<endl;
		if(sum==k){
			printf("%lld",tmp);
			return 0;
		}
		for(re i=1;i<=b;i++){
			if(sum+st.size()-1>=k&&tmp*p[i]>*st.rbegin())break;
			if(tmp<=maxn/p[i])
				st.insert(tmp*p[i]);
		}
		st.erase(it);
	}
}

所以考完之後,看一眼題解瞬間就明白了,直接用佇列維護,不用優先佇列,

使用15個優先佇列就夠了,

因為我們每次都取所有佇列頭的最小值,保證了每次取出來的都是最小的

那麼我們更新後面的答案的時候,只能向佇列頭最小的那個佇列的後面的佇列更新值

因為我們要去重,每一個這樣更新出來的值,

一定是由一個質數乘上一個佇列頭最小值出來的,

而這個佇列頭又是由他的前面的佇列頭的最小值更新的,

這樣每次都是小的乘大的,直接保證了不重不漏。。。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const ll inf=0x3f3f3f3f3f3f3f3f;
ll b,k;
ll p[25]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};
queue<ll> q[16];
signed main(){
	scanf("%lld%lld",&b,&k);
	for(re i=1;i<=b;i++)q[i].push(p[i]);
	ll mn,who;
	if(k==1){
		printf("1");
		return 0;
	}
	for(re i=1;i<k;i++){
		mn=inf,who;
		for(re j=1;j<=b;j++)
			if(mn>q[j].front())
				mn=q[j].front(),who=j;
		q[who].pop();
		for(re j=who;j<=b;j++){
			q[j].push(mn*p[j]);
		}
	}
	printf("%lld",mn);
}

T2 six

這個題真的是神題,二維狀壓,真牛逼!!!

我是從這個題才第一次接觸到二維狀壓的

所以其實這個題我是在zxb的指點之下才明白的,原來這個題是這麼壓的

對於一般的狀壓來說,我們只需要壓這一位是否出現了,

但是這個題他不一樣,因為你無法區分2、3和6

當你要加入一個6的時候,你發現當前的序列裡已經有了2、3這兩個質數

如果就是2、3這兩個數,那麼6就不能放進去了,但是如果是6的話,那還可以繼續放

這是我們最難區分的點,所以我們需要把這兩種情況分開,

對於每個可以放到序列裡的數來說,最多包含6個質數,所以我們就直接標記他的每一個質數

這時候你肯定會大叫,這什麼玩意,舉個例子:

B為6,那麼2是他的第一個質因子,3是第二個

2:01=1

3:10=2

6:11=3

我們把這些數拆成這樣的狀態,所以這就是為什麼資料範圍只有6

我們再對這些拆分進行狀壓,判斷這樣的數是否存在,直接用map記憶化搜尋就完了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int 
#define ll long long
#define pa pair<long long,long long>
#define mpa(x,y) make_pair(x,y)
#define fi first
#define se second
const ll mod=1e9+7;
ll n;
map<pa,ll> mp;
ll sum[1<<6],id[1<<6];
pa ys[10];
int top;
ll dfs(ll s1,ll s2){
	map<pa,ll>::iterator it=mp.find(mpa(s1,s2));
	if(it!=mp.end())return it->se;
	mp.insert(mpa(mpa(s1,s2),1));
	it=mp.find(mpa(s1,s2));
	for(re i=1;i<(1<<top);i++){
		int num=0;
		bool flag=0;
		for(re j=1;j<(1<<top);j++){
			if(!(i&j))continue;
			if((s1>>j-1)&1)num++;
			if((s2>>j-1)&1)flag=1;
			if(flag||num>=2)break;
		}
		if(flag||num>=2)continue;
		if((s1>>i-1)&1)it->se=(it->se+sum[i]*dfs(s1^(1ll<<i-1),s2|(1ll<<i-1))%mod)%mod;
		else it->se=(it->se+sum[i]*dfs(s1|(1ll<<i-1),s2)%mod)%mod;
	}
	return it->se;
}
signed main(){
	scanf("%lld",&n);
	for(ll i=2;i*i<=n;i++){
		if(n%i)continue;
		ys[++top].fi=i;
		while(n%i==0){
			ys[top].se++;
			n/=i;
		}
	}
	if(n!=1)ys[++top].fi=n,ys[top].se=1;
	for(re i=1;i<=top;i++)id[1<<i-1]=i;
	sum[0]=1;for(re i=1;i<(1<<top);i++)
		sum[i]=sum[i^(i&(-i))]*ys[id[i&(-i)]].se%mod;
	printf("%lld",dfs(0,0)-1);
}

考場上我直接去容斥了,然後發現,這個好像和順序有關,2、3、6不行,但是2、6、3可以

T3 walker

這個簡單哈哈哈,雖然我考場上寫都沒寫

但是我還沒見過這麼出題的,竟然讓我搞隨機化,讓我列舉50組資料,一定能找到答案

還統計了一下失敗的概率,這就靠人品了,rp++

就每次列舉兩組資料,直接高斯約旦求解

注意找這個角度的時候,判斷是正的還是負的,要不然WA死

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int 
#define ll long long
#define pa pair<long long,long long>
#define mpa(x,y) make_pair(x,y)
#define fi first
#define se second
const ll mod=1e9+7;
ll n;
map<pa,ll> mp;
ll sum[1<<6],id[1<<6];
pa ys[10];
int top;
ll dfs(ll s1,ll s2){
	map<pa,ll>::iterator it=mp.find(mpa(s1,s2));
	if(it!=mp.end())return it->se;
	mp.insert(mpa(mpa(s1,s2),1));
	it=mp.find(mpa(s1,s2));
	for(re i=1;i<(1<<top);i++){
		int num=0;
		bool flag=0;
		for(re j=1;j<(1<<top);j++){
			if(!(i&j))continue;
			if((s1>>j-1)&1)num++;
			if((s2>>j-1)&1)flag=1;
			if(flag||num>=2)break;
		}
		if(flag||num>=2)continue;
		if((s1>>i-1)&1)it->se=(it->se+sum[i]*dfs(s1^(1ll<<i-1),s2|(1ll<<i-1))%mod)%mod;
		else it->se=(it->se+sum[i]*dfs(s1|(1ll<<i-1),s2)%mod)%mod;
	}
	return it->se;
}
signed main(){
	scanf("%lld",&n);
	for(ll i=2;i*i<=n;i++){
		if(n%i)continue;
		ys[++top].fi=i;
		while(n%i==0){
			ys[top].se++;
			n/=i;
		}
	}
	if(n!=1)ys[++top].fi=n,ys[top].se=1;
	for(re i=1;i<=top;i++)id[1<<i-1]=i;
	sum[0]=1;for(re i=1;i<(1<<top);i++)
		sum[i]=sum[i^(i&(-i))]*ys[id[i&(-i)]].se%mod;
	printf("%lld",dfs(0,0)-1);
}