AtCoder Beginner Contest 253 C~F题解

AtCoder Beginner Contest 253

C (STL使用)

题意:初始有一个空集合 S,有三种操作:

1.插入 x

  1. 删除 cx
  2. 询问当前集合中最大值-最小值

直接根据题意模拟即可,我是用优先队列实现的,官方题解更优雅。

官方code:

#include 
using namespace std;

int main() {
    int q;
    cin >> q;
    multiset<int> st;
    while (q--) {
        int t;
        cin >> t;
        if (t == 1) {
            int x;
            cin >> x;
            st.insert(x);
        } else if (t == 2) {
            int x, c;
            cin >> x >> c;
            while (c-- and st.find(x) != st.end()) {
                st.erase(st.find(x));
            }
        } else {
            cout << *st.rbegin() - *st.begin() << endl;
        }
    }
}

mycode:

#include

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;
const int N=4e5+10;

void work()
{
	priority_queue<int> q1;
	priority_queue<int,vector<int>,greater<int> > q2;
	map<int,int> mp;
	int m;
	cin>>m;
	while(m--){
		int op,x,c;
		cin>>op;
		if(op==1){
			cin>>x;
			mp[x]++;
			q1.push(x);
			q2.push(x);
		}
		else if(op==2){
			cin>>x>>c;
			mp[x]-=c;
			if(mp[x]<=0) {
				mp[x]=0; 
			}
		}
		else {
			int x,y;
			while(q1.size()){
				if(mp[q1.top()]){
					x=q1.top();
					break;
				}
				q1.pop();
			}
			while(q2.size()){
				if(mp[q2.top()]){
					y=q2.top();
					break;
				}
				q2.pop();
			}
			cout<<x-y<<endl;
		}
	}
}
signed main()
{
	ios;
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	return 0;
}

D (数学,思维)

题意:求出 1~N 中所有既不是 A 的倍数,也不是 B 得倍数的数的和。

正难则反,求出是 AB 的倍数的和,然后用总的减去该值即可。

注意不要重复计算。

code:

#include

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;
const int N=4e5+10;

void work()
{
	int n,a,b;
	cin>>n>>a>>b;
	int d=__gcd(a,b);
	int x=n/a,y=n/b,z=n/(a*b/d);
	int sum=(x+1)*x/2*a+(y+1)*y/2*b-(z+1)*z/2*(a*b/d);
	int ans=(n+1)*n/2-sum;
	cout<<ans<<endl;
}

signed main()
{
	ios;
	int t;
	//cin>>t;
	t=1;
	while(t--)
	{
		work();
	}
	
	return 0;
}

E (前缀和优化dp,计数)

题意:给定n,m,k,求出共有多少种序列满足:

  1. 长度为n
  2. 1 ≤ A i ≤ M 1\leq A_i \leq M 1AiM , 对于任意的 i
  3. ∣ A i − A i − 1 ∣ ≥ k \vert A_i-A_{i-1} \vert \geq k AiAi1k

dp, d p [ i ] [ j ] 表示选 i 个数,并且最后一个数为 j 的方案数 dp[i][j]表示选i个数,并且最后一个数为j的方案数 dp[i][j]表示选i个数,并且最后一个数为j的方案数,则状态转移方程就很明显了:

for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int x=1;x<=j-k;x++){
				dp[i][j]+=dp[i-1][x];
				dp[i][j]%=mod;
			}
			for(int x=j+k;x<=m;x++){
				dp[i][j]+=dp[i-1][x];
				dp[i][j]%=mod;
			}
		}
	}

但是会发现时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2) ,所以考虑如何去优化。

转移过程中使用了上一层某个区间的和,因此我们不妨把上一层前缀和计算出来,然后可以做到 O ( 1 ) O(1) O(1) 算出某一区间的和。

注意特判 k = = 0 k==0 k==0 时的情况。

code:

#include

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=1e3+10,M=5e3+10,mod=998244353;
int s[M];
int dp[N][M];
int n,m,k;

void work()
{
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++) dp[1][i]=1;

	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			s[j]=(s[j-1]+dp[i-1][j])%mod; //单独把前一层的前缀和算出来
		}
		for(int j=1;j<=m;j++){
			if(k==0){//特判k=0
				dp[i][j]=(dp[i][j]+s[m])%mod;
			}
			else {
				int l=j-k,r=j+k;
				if(l>=1) dp[i][j]=(dp[i][j]+s[l])%mod;
				if(r-1<=m) dp[i][j]=((dp[i][j]+s[m]-s[r-1])%mod+mod)%mod;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=m;i++){
		ans=(ans+dp[n][i])%mod;
	}
	cout<<ans<<endl;
	
}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--) work();

	return 0;
}

F (离线树状数组,思维)

题意:给定一个 n ∗ m n*m nm 的矩阵,初始元素都为0,进行下列操作:

  1. l 到 r l到r lr 列的所有元素都加上 x
  2. 把第 i 行的元素全变为 x
  3. 查询 第 i 行,第 j 列 第i行,第j列 i行,第j 的元素值

如果没有操作2,可以使用树状数组来对差分数组操作来实现区间加。那么考虑操作2呢?我们可以继续用树状数组来维护总的前缀和,即所有操作1所加上的数的总和。假设对于某一行,查询操作前的最后一次操作2时的前缀为sum1,而进行查询时的前缀为sum,则其真实值为 x + s u m − s u m 1 x+sum-sum1 x+sumsum1 , x为操作2把该行变成的值。

同时注意怎么转化为离线操作。

code:

#include

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 tr[N];
struct node{
	int op,a,b,c;
}q[N];
int pos[N];
vector<int> v[N];
int ans[N];
int lowbit(int x)
{
	return x&-x;
}

void add(int x,int v)
{
	for(int i=x;i<N;i+=lowbit(i)) tr[i]+=v;
}
int sum(int x)
{
	int ans=0;
	for(int i=x;i;i-=lowbit(i)) ans+=tr[i];
	return ans;
}
void work()
{
	int n,m,t;
	cin>>n>>m>>t;
	
	for(int i=1;i<=t;i++){
		int x,y,z,val;
		cin>>x>>y>>z;
		q[i].op=x;
		q[i].a=y; q[i].b=z;
		if(x==1){
			cin>>val;
			q[i].c=val;
		}
		else if(x==2){ //对于每一行
			pos[y]=i; //记录这次操作2是在什么时候操作的
		}
		else {						//对于每一行
			v[pos[y]].push_back(i); //记录上一次操作2后,什么时候询问(可能有多次)
		}
	}
	// 离线处理查询
	for(int i=1;i<=t;i++){
		if(q[i].op==1) {
			add(q[i].a,q[i].c); add(q[i].b+1,-q[i].c);//维护前缀和
		}
		else if(q[i].op==2){//遇到操作2,就把这之前的前缀减去
			for(auto id:v[i]) ans[id]=q[i].b-sum(q[id].b);
		}
		else cout<<ans[i]+sum(q[i].b)<<endl;//加上总的前缀,即为答案
	}

}
signed main()
{
	int t;
	//cin>>t;
	t=1;
	while(t--) work();

	return 0;
}

你可能感兴趣的:(算法,c++,图论)