20211106NOIP模拟赛
前言
芜湖起飞!!国际金牌出的题就是比国内金牌出的题好做!!!
成功的狗到了160分(据说T2输出0有十分??)
T1
一道逆元题:
得分:\(\color{green}{100}\)
考场上青禾巨佬12分钟切掉(但被卡了10分),本蒟蒻两个半小时才做对\(QWQ\)
分析题意,我们可以把它转换为抛骰子的问题,为抛到一种情况的期望次数。
像抛到6的期望就是抛到6的概率的倒数,就是6次。
再来看这道题,序列有序的概率是\(\frac{1}{序列的排列个数}\),所以期望次数就是序列的排列次数。
我们又开始求序列的排列个数了,由于有重复元素所以不好求。而蒟蒻用打表的方法找到了规律!!!
\[序列排列个数=\frac{n!}{\prod_{i=1}^{n}{cnt[val[i]]!}}
\]
就这样,蒟蒻水题成功!(但还是因为不会打逆元而卡住了两个小时)
逆元就\(powfast\)求一下。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
map<int,int> q;
int n,a[1000001];
long long mul[1000001],frac=1;
bool flag=1;
void init(){
mul[0]=1;
mul[1]=1;
for(int i=2;i<=n;i++){
mul[i]=(long long)mul[i-1]*i%mod;
mul[i]%=mod;
}
return ;
}
inline int read(){
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-'){
sign=-1;
}
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
int temp[1000001];
long long tot=0;
long long fast_pow(long long x,long long y){
long long res=1;
while(y){
if(y&1){
res=res*x%mod;
}
x=x*x%mod;
y>>=1;
}
return res;
}
signed main(){
freopen("bogo.in","r",stdin);
freopen("bogo.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
if(i>1){
if(a[i]<a[i-1]){
flag=0;
}
}
}
init();
if(flag){
putchar('0');
return 0;
}else{
memset(temp,0,sizeof temp);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
q[a[i]]++;
}
int qq=0;
tot=mul[n]%mod;
for(int i=1;i<=n;i++){
if(a[i]!=a[i-1]){
temp[++qq]=q[a[i]];
}
}
frac=1;
for(int i=1;i<=qq;i++){
if(temp[i]==1){
continue;
}else{
frac=frac*(mul[temp[i]])%mod;
frac%=mod;
}
}
cout<<((long long)(tot)%mod*fast_pow(frac,mod-2)%mod)%mod<<endl;
}
return 0;
}
T2
得分:\(\color{red}{0}\)
蒟蒻考场上在做完T1后,先把T3的暴力打了50分(加O2卡到60分),然后倒回来做T2,看题觉得是DP但是懒得推,于是搜索打挂了。
分析:首先,我们用一个三维数组来维护,在前n的数中,我们拿走了j个权值为1的点,还有k个权值为2的点,所浪费的空间。递推后,再贪心算空间。
\(O(n^3)\)
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,w,a[10001],su,q[10001];
short f[405][405][405];
int c[3];
int main()
{
scanf("%d %d",&n,&w);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
q[i]=q[i-1]+a[i];
su+=a[i],++c[a[i]];//su 前缀和,c统计次数
}
if(su<=w){
cout<<0<<endl;
return 0;
}
memset(f,127/3,sizeof f);
f[0][0][0]=0;
auto upd=[&](short&a,int b){
if(a>b) a=b;
};
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
for(int k=0;j+k<=i;k++){
if(f[i][j][k]<=8000){
int tw=q[i]-j-k*2+f[i][j][k];
if(tw%w+a[i+1]<=w){//不用开新的包,浪费和原来一样
upd(f[i+1][j][k],f[i][j][k]);
}else{
upd(f[i+1][j][k],f[i][j][k]+w-tw%w);//开新包,加上浪费的空间
}
if(a[i+1]==1){
upd(f[i+1][j+1][k],f[i][j][k]);//拿走下一个
}
if(a[i+1]==2){
upd(f[i+1][j][k+1],f[i][j][k]);
}
}
}
}
}
pair<int,int> ans(2e9,2e9);
for(int j=0;j<=n;j++){
for(int k=0;j+k<=n;k++){
if(f[n][j][k]>=0){
int tw=q[n]-j-k*2+f[n][j][k];//放的总空间
int K=k;
int J=j;//贪心把拿出来的放进去,
while(K){
if(tw%w+2<=w) --K,tw+=2;//先放大的
else if(J) --J,tw+=1;//再放小的补位
else --K,tw+=3;//没有小的就假装有小的打包一起放
}
while(J) --J,tw+=1;//放入
ans=min(ans,pair<int,int>((tw+w-1)/w,j+k));//比较用包的个数
}
}
}
cout<<ans.second<<endl;
return 0;
}
T3
得分:\(\color{orange}{60}\)
算法一:暴力搜索:
期望得分:50~60分
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
long long weight[100001];
char sit[100001];
int book[100001];
struct node{
long long sum;
char op;
}e[100001];
bool dfs(int num,long long left,long long right){
if(num>n){
for(int i=1;i<=n;i++){
printf("%lld %c\n",e[i].sum,e[i].op);
}
return 1;
}
int flag=0;
if(sit[num]=='L'){
for(int i=1;i<=n;i++){
if(!book[i]&&left+weight[i]>right){
book[i]=1;
e[num].sum=weight[i];
e[num].op='L';
flag=dfs(num+1,left+weight[i],right);
if(flag){
return 1;
}
book[i]=0;
}
if(!book[i]&&left>weight[i]+right){
book[i]=1;
e[num].sum=weight[i];
e[num].op='R';
flag=dfs(num+1,left,right+weight[i]);
if(flag){
return 1;
}
book[i]=0;
}
}
}else{
for(int i=1;i<=n;i++){
if(!book[i]&&left<right+weight[i]){
book[i]=1;
e[num].sum=weight[i];
e[num].op='R';
flag=dfs(num+1,left,right+weight[i]);
if(flag){
return 1;
}
book[i]=0;
}
if(!book[i]&&left+weight[i]<right){
book[i]=1;
e[num].sum=weight[i];
e[num].op='L';
flag=dfs(num+1,left+weight[i],right);
if(flag){
return 1;
}
book[i]=0;
}
}
}
return 0;
}
inline int read(){
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-'){
sign=-1;
}
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
int main(){
freopen("weight.in","r",stdin);
freopen("weight.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
weight[i]=(long long)read();
}
sort(weight+1,weight+n+1);
cin>>sit+1;
dfs(1,0,0);
return 0;
}
算法二:
很妙的方法:
就是,如果连续相同的条件,我们就一边放\(a_{m-i-1}\),一边放\(a_{m-i}\),如果不一样,就在一边加\(a_{m+i}\),一边放\(a_{m+i+1}\)
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,a[100001],b[100001];
char c[100001];
string s;
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
cin>>s;
sort(a,a+n);
a[n]=2e9;
int r=-1;
int op=0;
for(int i=0;i<n;i++){
if(i&&s[i]==s[i-1]){
++r;//确定m的位置
}
}
int l=r+1;
for(int i=0;i<n;i++){
if(i&&s[i]==s[i-1]){
c[i]=op^=1;//上一个操作异或
b[i]=a[r--];//a_m-1
}else{
c[i]=(s[i]=='R');
b[i]=a[l++];//a_m+i+1
if(!i) op=(s[i]=='R');
}
}
for(int i=0;i<n;i++)
{
cout<<b[i]<<' '<<("LR"[c[i]])<<"\n";
}
return 0;
}
好的,蒟蒻非常开心\(QWQ\)