C (STL使用)
题意:初始有一个空集合 S,有三种操作:
1.插入 x
直接根据题意模拟即可,我是用优先队列实现的,官方题解更优雅。
官方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 得倍数的数的和。
正难则反,求出是 A 或 B 的倍数的和,然后用总的减去该值即可。
注意不要重复计算。
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,求出共有多少种序列满足:
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 n∗m 的矩阵,初始元素都为0,进行下列操作:
如果没有操作2,可以使用树状数组来对差分数组操作来实现区间加。那么考虑操作2呢?我们可以继续用树状数组来维护总的前缀和,即所有操作1所加上的数的总和。假设对于某一行,查询操作前的最后一次操作2时的前缀为sum1,而进行查询时的前缀为sum,则其真实值为 x + s u m − s u m 1 x+sum-sum1 x+sum−sum1 , 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;
}