AtCoder Beginner Contest 247
题意:序列S=1, S n = S n − 1 , n , S n − 1 S_n=S_{n-1},n,S_{n-1} Sn=Sn−1,n,Sn−1, 即 S 1 = 1 , S 2 = 1 , 2 , 1 S_1=1, S_2=1,2,1 S1=1,S2=1,2,1, 给出n,求 S n S_n Sn为多少·。
1 ≤ N ≤ 16 1 \leq N \leq 16 1≤N≤16.
由于N比较小,因此我们可以暴力的dfs来输出,主要是对递归的理解,当然还有其他的方法。
code:
#include<bits/stdc++.h>
using namespace std;
vector<int> v;
void dfs(int n)
{
if(n==1){
v.push_back(1);
return ;
}
dfs(n-1);
v.push_back(n);
dfs(n-1);
}
signed main()
{
int n;
cin>>n;
dfs(n);
for(auto vi:v){
cout<<vi<<" ";
}
return 0;
}
题意:给定一个队列,初始为空,执行下面两种操作:
- 往队尾插入c个价值为x的球。
- 取出队头前c个球,并输出这c个球的价值。
1 ≤ c ≤ 1 0 9 1 \leq c \leq 10^9 1≤c≤109
考虑使用双端队列,由于c比较大,暴力插入肯定不行,因此用pair来记录每个球的数量。然后就是根据题意来模拟啦。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> pii;
vector<int> v;
signed main()
{
deque<pii> dq;
int q,tt;
cin>>q;
int x,c;
while(q--){
cin>>tt;
if(tt==1){
cin>>x>>c;
dq.push_back({c,x});
}
else
{
int ans=0;
cin>>c;
while(c>0)
{
auto t=dq.front();
dq.pop_front();
if(t.first<=c){
ans+=t.second*t.first;
c-=t.first;
}
else {
ans+=t.second*(c);
dq.push_front({t.first-c,t.second});
c=0;
}
}
cout<<ans<<endl;
}
}
return 0;
}
题意:给定序列A,问共有多少对(L,R),满足:
- 1 ≤ L , R ≤ N 1 \leq L,R \leq N 1≤L,R≤N.
- A L , A L + 1 . . . A R A_L,A_{L+1}...A_R AL,AL+1...AR 的最大值为X, 最小值为Y.
做的时候没想那么多,直接对着样例开始模拟了起来,做完发现这题竟然那么多解法。不过我的解法也很不错。
大致思路就是类似于双指针,固定区间的右端点,然后找满足条件的最远的左端点。例如当前右端点为R,合法的最远左端点为L,则这个点对答案的贡献为R-L,。
具体怎么实现呢?我们可以从前往后扫描,分别记录下 X, Y,以及比X大或比Y小的数字的位置,然后计算贡献即可。这样说也不是很好理解,可以根据代码对最后一个样例模拟,会好理解一些。
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;
//head
const int N=2e5+10;
int a[N];
void work()
{
int n,x,y;
cin>>n>>x>>y;
int px=0,py=0,p=0;
int ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]<y||a[i]>x) p=i;
if(a[i]==x) {
px=i;
}
if(a[i]==y){
py=i;
}
//cout<<px<<"###"<<py<<endl;
if(px&&py){
int mn=min(px,py);
ans+=max(0LL,mn-p);
}
}
cout<<ans<<endl;
}
signed main()
{
//ios;
int t;
t=1;
//cin>>t;
while(t--) work();
return 0;
}
题意:有N张卡片,正反面都印着数字,正面印的数字和反面印的数字都是N的一个排列,问有多少种方案,使得取出的球正面和反面的数字构成N的一个排列。
这个题还是需要来发掘一些性质的,我们把它抽象出来,即可看成正反面代表连一条边。 这是个很关键的性质,说明整个图是由一个个独立的环构成的,根据乘法原理,总的方案数等于各个环的方案数的乘积。
那一个点数为N的环的方案数怎么求呢?我们可以采用递推式来求,
设 d p [ i ] 为当前有 i 个点,且构成一个环的方案数,那么对于 d p [ i ] , 由谁转移过来呢?因为每个点连接着两个点,因此可以由 d p [ i − 2 ] 和 d p [ i − 1 ] 转移过来,可以画图方便理解 设dp[i]为当前有i个点,且构成一个环的方案数,那么对于dp[i],由谁转移过来呢?因为每个点连接着两个点,因此可以由dp[i-2]和dp[i-1]转移过来,可以画图方便理解 设dp[i]为当前有i个点,且构成一个环的方案数,那么对于dp[i],由谁转移过来呢?因为每个点连接着两个点,因此可以由dp[i−2]和dp[i−1]转移过来,可以画图方便理解
然后我们使用并查集来判断每个环的大小即可。感觉这题难的还是在于怎么求每个环的方案数,
code:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;
//head
const int N=2e5+10,mod=998244353;
int a[N],b[N];
int f[N],p[N];
int cnt[N];
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void work()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
f[1]=1; f[2]=3;
for(int i=3;i<=n;i++){
f[i]=(f[i-1]+f[i-2])%mod;
}
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=n;i++){
int pa=find(a[i]),pb=find(b[i]);
if(pa!=pb) p[pb]=pa;
}
for(int i=1;i<=n;i++){
cnt[find(i)]++;
}
int ans=1;
for(int i=1;i<=n;i++){
if(cnt[i]){
ans=ans*f[cnt[i]]%mod;
}
}
cout<<ans<<endl;
}
signed main()
{
//ios;
int t;
t=1;
//cin>>t;
while(t--) work();
return 0;
}