模拟赛3

感觉这几次考试有好多暴搜,对我这个不会暴搜的蒟蒻很不友好(泪

T1:李时珍的皮肤衣

找规律水题,第\(N\)层皮肤衣被晒到的天数实际上是前面从1到N-1层被晒到天数的总和再加上1,然后再加上最初穿上衣服的那一天,显然能得出\((2^{n-1}+1)\%n\)的式子,水过。

T2:马大嘴的废话

题意是给\(n\)个长度不超过\(20\)的字符串,求m个字串中某一字串的出现次数。考场上看了三分钟果断暴力hash,正解什么的都没怎么去想...由于\(n \le 10000\), \(m \le 100000\),,,最后只拿了\(60pts\),符合预期。

不过咱是真没想到\(map\)映射(泪 \(hxq\) \(yyds!\)

因为每个字符串的长度不超过\(20\),所以能处理出每个字串是否出现,若出现,则这个子串的映射值累加,(直接暴力A上 \(O(n^2/2)\),输入\(M\)个字串时,只需要输出该字串的映射值。注意再加一个字符串——布尔map映射,这样能保证每个字符串最多的贡献为\(1\),每次输入要清空。

#include <bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int maxn=20010,maxm=200010;
int N,M,len1[maxn],len2[maxm],cnt[maxm];
char k1[maxn];
char k2[maxm][25];
ull f1[maxn][25];
ull f2[maxm];
map <string,int> p;
map <string,bool> l;
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}

int main(){
	freopen("mdz.in","r",stdin);
	freopen("mdz.out","w",stdout);
	N=read();
	for(int i=1;i<=N;++i) {
		scanf("%s",k1+1);
		len1[i]=strlen(k1+1);
		l.clear();
		for(int j=1;j<=len1[i];j++){
			string lp;
			for(int k=j;k<=len1[i];k++){
				lp+=k1[k];
				if(!l[lp]){
					p[lp]++;
					l[lp]=true;
				}
			}
		}
	}
	M=read();
	for(int i=1;i<=M;++i) {
		string op;
		cin>>op;
		printf("%d\n",p[op]);
                //麻
	}
	return 0;
}

然而正解是\(trie\)树。😭

T3:SSY的队列

题意:班级里有N个身高互不相同的同学,请你求出这N个人的所有排列中任意两个相邻同学的身高差均不为给定整数M的倍数的排列总数。

我一直以为这是一个纯数学问题,思路就是将不能相邻的小组依次插到可以相邻的空间里面,然后就只剩下求排列数的操作。(我甚至以为这个比T2和T4简单,,,

于是我萎了。

插的次序不同会导致方案不同,这个思路显然会漏掉不少方案,\(10pts\)

正解:将每个\(\%M\)余数相同的人归为一类(只有一个人的要都放在一起),因为除第一组外的组之间不能相邻 ,故先用\(ans\)乘以每一组人数的阶乘(求排列,然后这个问题就转化为了每组人数的问题,暴搜解决,\(hash\)映射的是每一次搜索的状态,以便记忆化。。。

代码:

#include <bits/stdc++.h>
#define int long long
//麻了,竟然不是数学问题(泪 
using namespace std;
const int maxn=200;
const int mod=1234567891;
int N,M,cnt,num,maxx,ans=1,base=131;
int tall[maxn];
int lasker[maxn];
int b[maxn];
int ark[maxn];
bool vis[maxn];
map <int,int> mp[maxn];
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
void pre(){
	ark[0]=ark[1]=1;
	for(int i=1;i<=100;i++) ark[i]=ark[i-1]*i%mod;
	for(int i=0;i<=num;i++) ans=ans*ark[lasker[i]]%mod;
	return;
}
int dfs(int now,int fas){
	if(now>N) return 1;//超出人数范围 
	memset(b,0,sizeof(b));
	for(int i=1;i<=num;i++){
		if(i!=fas) b[lasker[i]]++;
	}
	//同人数的组归为一类(不包括第一组 
	int ans1=lasker[0],ans2=0;
	for(int i=0;i<=maxx;i++) ans1=ans1*base+b[i];
	//hash映射状态 
	 ans1=ans1*base+lasker[fas];
	 if(mp[now].find(ans1)!=mp[now].end()) return mp[now][ans1];
	 //记忆化
	 if(lasker[0]>0){
	 	lasker[0]--;
	 	ans2=ans2+dfs(now+1,0)%mod;
	 	lasker[0]++;
	 } 
	 for(int i=1;i<=num;i++){
	 	if(i!=fas&&lasker[i]>0){
	 		lasker[i]--;
	 		ans2=ans2+dfs(now+1,i)%mod;
	 		lasker[i]++;
		 }
	 } 
	 //暴搜 
	 mp[now][ans1]=ans2;
	 return ans2%mod;
} 
signed main(){
	freopen("ssy.in","r",stdin);
	freopen("ssy.out","w",stdout);
	N=read();
	for(int i=1;i<=N;++i) tall[i]=read();
	M=read();
	for(int i=1;i<=N;i++) tall[i]=(tall[i]%M+M)%M;
	for(int i=1;i<=N;i++){
		if(vis[i]) continue;
		vis[i]=1;
		cnt=0;
		for(int j=i;j<=N;++j){
			if(tall[i]==tall[j]){
				vis[j]=1;
				cnt++;
			}
		}
		maxx=max(maxx,cnt);
		if(cnt==1) lasker[0]++;
		//无限制的放到0里面 
		else lasker[++num]=cnt;
		//有限制的归为一组 
	}
	pre();
	ans=ans*dfs(1,0)%mod;
	//暴搜(泪 
	printf("%lld",ans);
	return 0;
}

T4:清理牛棚

考场上实在没想出来,居然用分块加纯贪心的假思路骗了\(50pts\)(((,数据太水力。

机房里的大佬们讲出了这道题至少5种做法,我该怎么膜拜他们呢。

只列举两种。

最短路(来自\(smwty\)大佬!):

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100010,maxm=2000,INF=999999999;
int N,M,E,T,cnt;
ll dis[maxn],ans;
int head[maxn];
bool vis[maxn];
struct ED{
	int to,next,w;
}e[maxn*100];
inline void add(int u,int v,int w){
	e[++cnt].to=v;
	e[cnt].w=w;
	e[cnt].next=head[u];
	head[u]=cnt;
	return;
}
struct Times{
	int l,r,s;
	bool operator<(const Times &a)const{
		if(r==a.r) return l<a.l;
		return r<a.r;
	}
}cow[maxn];  
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
void spfa(int u){
	dis[u]=0;
	queue<int> q;
	q.push(u);
	while(!q.empty()){
		int k=q.front();
		vis[k]=0;
		q.pop();
		for(int i=head[k];i;i=e[i].next){
			int v=e[i].to,w=e[i].w;
			if(dis[v]>dis[k]+w){
				dis[v]=dis[k]+w;
				if(!vis[v]){
					q.push(v);
					vis[v]=1;
				}
			}
		}
	} 
} 
int main(){
	freopen("clean.in","r",stdin);
	freopen("clean.out","w",stdout);
	N=read();M=read();E=read();E++;
	for(int i=1;i<=N;++i){
		cow[i].l=read();cow[i].r=read();cow[i].s=read();
		add(cow[i].l,cow[i].r+1,cow[i].s);
		//+1保证没有类似(0,0)这种情况的连边 
	}
	for(int i=M;i<=E;i++) add(i,i-1,0),dis[i]=INF;
	//可以让最短路连接下去 
	spfa(M);
	if(dis[E]==INF) ans=-1;
	else ans=dis[E];
	printf("%lld",ans); 
	return 0;
}

DP(来自拿下\(Rank1\)\(kiritokazuto\)大佬!):

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100010,maxm=2000,INF=999999999;
int N,M,E,T;
ll dp[maxn];
ll ans;
struct Times{
	int l,r,s;
	bool operator<(const Times &a)const{
		if(r==a.r) return l<a.l;
		return r<a.r;
	}
}cow[maxn];  
inline int read(){
	int s=0,w=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return s*w;
}
int main(){
	freopen("clean.in","r",stdin);
	freopen("clean.out","w",stdout);
	N=read();M=read();E=read();
	for(int i=1;i<=N;++i){
		cow[i].l=read();cow[i].r=read();cow[i].s=read();
	}
	ans=INF;
	sort(cow+1,cow+1+N);
	memset(dp,0x7f,sizeof(dp));
	for(int i=1;i<=N;++i){
		if(cow[i].l==M) dp[i]=cow[i].s;
		for(int j=i-1;cow[j].r>=cow[i].l-1&&j>=1;j--){
			dp[i]=min(dp[i],dp[j]+cow[i].s);
			//在区间连接的前提下找最小花费 
		}
		if(cow[i].r==E) ans=min(dp[i],ans);
	}
	if(ans==INF) ans=-1;
	printf("%lld",ans);
	return 0;
}

我写完了。

posted @ 2022-05-15 17:27  Broken_Eclipse  阅读(21)  评论(0)    收藏  举报

Loading