模拟赛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;
}
我写完了。

浙公网安备 33010602011771号