前三道签到题就不说了,剩下的题都挺有教育意义的
题目D:小红数组操作——>有两个操作:序号1是输入x,y将x插入在y的右边。保证此时数组没有元素等于x,且数组中存在一个y,当y等于0则把x插在最左边。序号2是输入x,将元素x删除
思路:刚一拿到这一题你会想到是链表,但是链表是根据idx(第几个插入的数进行添加删除的)但这里是具体的数值,我们可以引入一个map<int,int>key是原本的数,val是它对应idx的值这样一来我们就可以通过map来找要在那个位置插入了,单双链表都可以,赛时在acwing 上的双,但单也可以实现,对于删除一个值 我们不把它真正意义上的删去而是直接改变它的e值相当于做一个标记就好
单链表
#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
const int N = 2e5+10;
int e[N],ne[N],idx=1,head=-1;
void add(int k,int x)
{
if(k)
{
mp[x]=idx;
e[idx]=x,ne[idx]=ne[k],ne[k]=idx++;
}
else
{
mp[x]=idx;
e[idx]=x,ne[idx]=head,head=idx++;
}
}
int main()
{
int q;cin>>q;
int n=0;
while(q--)
{
int op,x,y;
cin>>op;
if(op==1)
{
n++;
cin>>x>>y;
add(mp[y],x);
}
else
{
n--;
cin>>x;
e[mp[x]]=-1;
}
}
cout<<n<<endl;
for(int i=head;~i;i=ne[i])
if(e[i]!=-1)cout<<e[i]<<' ';
}
双链表
#include<bits/stdc++.h>
using namespace std;
map<int,int>mp;
const int N = 2e5+10;
int e[N],l[N],r[N],idx;
void init()
{
//0为左端点,1为右端点;
r[0]=1,l[1]=0;
idx=2;
}
void add(int k,int x)
{
mp[x]=idx;
e[idx]=x;
l[idx]=k;
r[idx]=r[k];
l[r[k]]=idx;//18与19句不能写反
r[k]=idx;
idx++;
}
void del(int k)
{
r[l[k]]=r[k];
l[r[k]]=l[k];
}
int main()
{
init();
int q;cin>>q;
int cnt1=0,cnt2=0;
while(q--)
{
int op,x,y;
cin>>op;
if(op==1)
{
cnt1++;
cin>>x>>y;
if(y==0)add(0,x);
else
{
add(mp[y],x);
}
}
else
{
cnt2++;
cin>>x;
//del(mp[x]);
e[mp[x]]=-1;
}
}
cout<<cnt1-cnt2<<endl;
for(int i=r[0];i!=1;i=r[i])
if(e[i]!=-1)cout<<e[i]<<' ';
}
题目E:小红的子集取反(变种01背包)——>小红得到一个数组,她可以对其中的一些元素乘-1。要想使得最后元素和为0,问最少的操作次数,没有则输入-1
思路:首先它是一道dp题(咱就是説一点没看出来,光贪了服。dp[i][j]表示前i个数,使得最终元素和为j的数量的最小值
状态计算1:当前的数不乘-1-->dp[i][j]=min(dp[i][j],dp[i-1][j+x];
当前的数乘-1-->dp[i][j]=min(dp[i][j],dp[i-1][j-x]+1);加上一次操作次数
由于每一个x是-200~200的范围,他们最小达到了-40000,数组下标会越界,我们给二维统一加上一个偏移量
#include<bits/stdc++.h>//01背包
using namespace std;
int dp[210][80080];//第二维统一加了一个偏移量
//dp[i][j]:表示前i个数,使得最终元素和为j的数量的最小值
int main()
{
memset(dp,0x3f,sizeof dp);
dp[0][4000]=0;
int q;cin>>q;
for(int i=1;i<=q;i++)
{
int x;cin>>x;
for(int j=0;j<=8000;j++)
{
if(j+x>=0&&j+x<=8000)dp[i][j]=min(dp[i][j],dp[i-1][j+x]);
if(j-x>=0&&j-x<=8000)dp[i][j]=min(dp[i][j],dp[i-1][j-x]+1);//改变符号
}
}
if(dp[q][4000]>0x3f3f3f3f/2)cout<<-1<<endl;
else cout<<dp[q][4000]<<endl;
return 0;
}
题目F:小红的连续段(组合数学)——>她定义了连续段的数量为相同字符的极长连续子串的数量如“aabbaaa"能分成三段"aa","bb","aaa",现在她让你求出长度为x+y包含x个'a'和y个'b'组成的字符串连续段数量恰好为i的字符串数量。输入[1,x+y]中的第i个答案
思路:这是一个经典的“隔板法”模型,我们首先讨论形如“ababa..”类型的,那么a有i/2段,b有i/2段,这个可以形象的用这种模型:一串“aaa.."切cnt刀,的方案数,求组合数用到了快速幂求逆元(x,mod-2),和组合公式:C[n][m]=!n/(!m*!(n-m))
//隔板法模型
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD=1e9+7;
LL jc[2010];
long long qmi(LL a,LL b)
{
LL res=1;
while(b)
{
if(b&1)res=res*a%MOD;
b>>=1;
a=a*a%MOD;
}
return res;
}
long long inv(int x)
{
return qmi(x,MOD-2);
}
long long C(int n,int m)
{
if(m<0||n-m<0)return 0;//除去不合法方案
return jc[n]*inv(jc[m])%MOD*inv(jc[n-m])%MOD;
}
int main()
{
jc[0]=1;
for(int i=1;i<=2010;i++)
jc[i]=jc[i-1]*i%MOD;
int x,y;
cin>>x>>y;
for(int i=1;i<=x+y;i++)
{
int ji=i/2+i%2,ou=i/2;
//x个a,x-1个空隙,插的板子是要求的分隔数少1
cout<<(C(x-1,ji-1)*C(y-1,ou-1)+C(y-1,ji-1)*C(x-1,ou-1))%MOD<<endl;
}
return 0;
}