8.4考试总结(NOIP模拟30)[毛一琛·毛二琛·毛三琛]
最有名的莫过于想死一次吗。
前言
至今都不知道题目是个啥。。。
T1 毛一琛
解题思路
\(\mathcal{Meet\;In\;The\;Middle}\)
其实就是一个爆搜。。。
把整个区间分为两部分,每个部分有两个集合。
对于每一个数状态只有三种:集合1,集合2,不选。
然后对于已经两个区间内,如果每个区间中的两个集合价值之差相同的话,
这四个集合所组合成的两个集合一定可以分成两个值相同的集合。
然后就是对于了两半分别 DFS 就好了。。
- 注意:全部开 long long 或者 map套 vector会 TLE。
code
#include<bits/stdc++.h>
//#define int long long
using namespace std;
int n,ans,cnt,s[30];
bool vis[1<<21];
vector<int> v[1<<21];
unordered_map<int,int> mp;
void dfs(int opt,int x,int anc,int sum1,int sum2,int sta)
{
if(x==anc+1)
{
if(opt==1)
{
if(mp.find(abs(sum1-sum2))==mp.end()) return ;
int tmp=mp.find(abs(sum1-sum2))->second;
for(int i=0;i<v[tmp].size();i++)
vis[sta<<n/2|v[tmp][i]]=true;
}
else
{
if(mp.find(abs(sum1-sum2))==mp.end()) mp[abs(sum1-sum2)]=++cnt;
v[mp.find(abs(sum1-sum2))->second].push_back(sta);
}
return ;
}
dfs(opt,x+1,anc,sum1+((opt)?s[x+n/2]:s[x]),sum2,sta|(1<<x-1));
dfs(opt,x+1,anc,sum1,sum2+((opt)?s[x+n/2]:s[x]),sta|(1<<x-1));
dfs(opt,x+1,anc,sum1,sum2,sta);
}
signed main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
dfs(0,1,n/2,0,0,0);
dfs(1,1,n-n/2,0,0,0);
for(int i=1;i<(1<<n);i++) ans+=vis[i];
printf("%d",ans);
return 0;
}
T2 毛二琛
解题思路
非常好的一个题(至少对于我来说是这样的)
首先发现对于 swap 的顺序是有一定优先级的。。
然后我们循环求出每一个 swap 操作的优先级。
接下来 DP 定义 f[i][j] 为前 i 个 swap 操作中 第 i 个优先级排名为 j
就可以简单的写出 \(\mathcal{O(n^3)}\) 的 DP转移。
发现可以前缀和优化,然后就可以把复杂度降低到 \(\mathcal{O(n^2)}\)
可以通过此题。
- 注意:操作优先级以及 DP 的边界问题。
code
70pts
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<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=5e3+10,mod=1e9+7;
int n,ans,f[N][N],s[N],opt[N];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=read()+1;
for(int i=1;i<=n;i++)
{
if(s[i]-i>1)
{
if(!opt[i-2]) opt[i-2]=opt[i-3];
for(int j=i-1;j<=s[i]+1;j++)
opt[j]=opt[j-1]+1;
}
else
{
if(!opt[s[i]+1]) opt[s[i]+1]=opt[s[i]+2];
for(int j=i-2;j>=s[i];j--)
opt[j]=opt[j+1]+1;
}
}
f[1][1]=1;
for(int i=2;i<n;i++)
for(int j=1;j<i;j++)
{
if(opt[i]>opt[i-1])
for(int k=j+1;k<=i;k++)
f[i][k]=(f[i][k]+f[i-1][j])%mod;
if(opt[i]<opt[i-1])
for(int k=1;k<=j;k++)
f[i][k]=(f[i][k]+f[i-1][j])%mod;
if(opt[i]==opt[i-1])
for(int k=1;k<=i;k++)
f[i][k]=(f[i][k]+f[i-1][j])%mod;
}
for(int i=1;i<n;i++)
ans=(ans+f[n-1][i])%mod;
printf("%lld",ans);
return 0;
}
正解
#include<bits/stdc++.h>
#define int long long
#define f() cout<<"Pass"<<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=5e3+10,mod=1e9+7;
int n,ans,f[N][N],pre[N],s[N],opt[N];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=read()+1;
for(int i=1;i<=n;i++)
if(s[i]-i>1)
{
if(!opt[i-2]&&i-3>=0) opt[i-2]=opt[i-3];
for(int j=i-1;j<=s[i]+1;j++)
opt[j]=opt[j-1]+1;
}
else
{
if(!opt[i-1]&&i-2>=0) opt[i-1]=opt[i-2];
for(int j=i-2;j>=s[i];j--)
opt[j]=opt[j+1]+1;
}
f[1][1]=pre[1]=1;
for(int i=2;i<n;i++)
{
if(opt[i]>opt[i-1])
for(int k=2;k<=i;k++)
f[i][k]=(f[i][k]+pre[k-1])%mod;
if(opt[i]<=opt[i-1])
for(int k=1;k<i;k++)
f[i][k]=(f[i][k]+pre[i-1]-pre[k-1]+mod)%mod;
for(int j=1;j<=i;j++)
pre[j]=(pre[j-1]+f[i][j])%mod;
}
for(int i=1;i<=n-1;i++)
ans=(ans+f[n-1][i])%mod;
printf("%lld",ans);
return 0;
}
T3 毛三琛
解题思路
其实正解的时间复杂度是假的。
二分答案,查找每一个 x 下最小的最大值。
然后就是一些剪枝。。(不要开long long)
然后就没了。。(又水了一篇题解)(逃
code
#include<bits/stdc++.h>
#define ull unsigned long long
#define f() cout<<"Pass"<<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=1e4+10;
int n,m,ans=1e9,mod,sum,s[N],q[N];
bool check(int x)
{
int tot=1,res=0;
for(int i=1;i<=n;i++)
{
if(q[i]>x) return false;
if(res+q[i]<=x) res+=q[i];
else res=q[i],tot++;
if(tot>m) return false;
}
return tot<=m;
}
signed main()
{
n=read();
mod=read();
m=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int x=0;x<mod;x++)
{
sum=0;
for(int i=1;i<=n;i++)
{
q[i]=(x+s[i])%mod;
sum+=q[i];
}
if(!check(ans)) continue;
int l=0,r=sum+1,tmp=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)){tmp=mid;r=mid-1;}
else l=mid+1;
}
if(tmp!=-1) ans=min(ans,tmp);
}
printf("%d",ans);
return 0;
}