【补题计划】NOI Online 2022
【NOI Online 2022】补题记录
入门组
T1 【NOI Online 2022】王国比赛
就这lj小模拟一遍过
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int maxn=1010;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int n,m,tot;
int ans[maxn];
int tuice[maxn];
int a[maxn][maxn];
int main()
{
n=read();
m=read();
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
a[i][j]=read();
for(int i=1;i<=n;i++) ans[i]=read();
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
if(a[i][j]) tuice[j]++;
for(int i=1;i<=n;i++)
if(tuice[i]>=m-tuice[i]) tuice[i]=1;
else tuice[i]=0;
for(int i=1;i<=n;i++)
if(tuice[i]==ans[i]) tot++;
cout<<tot;
return 0;
}
T2 【NOI Online 2022】数学游戏
好难想到
但是理解起来很简单,代码也很短
这就是数学+思维题么
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=5e5+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int t;
int gcd(int x,int y)
{
return (x%y) ? gcd(y,x%y) : y;
}
signed main()
{
t=read();
while(t--)
{
int x=read();
int z=read();
if(z%x!=0)
{
cout<<-1<<endl;
continue;
}
int l=gcd(z/x,x*x);
int sq=(int)sqrt(l);
if(sq*sq!=l)
{
cout<<-1<<endl;
continue;
}
cout<<(z/x)/sq<<endl;
}
return 0;
}
T3 【NOI Online 2022】字符串
一眼就是DP啦,设DP[i][j][k][l]表示前i个字符,从左边删j个,右边删k个,留下l个
过不去的,但能优化
因为j+k+l=i,所以可以搞掉一维
一共四种情况
要是这一个该删,那就考虑从左边删还是从右边删
而且经过证明三种字符都是连续的(从左边删,不删,从右边删)
所以右边之前有要删的,那就新加进来的必须删
然后就是看一看要不要留下
最后就是要是全都是从左边删的那种字符那就可以从后边加从左边删
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=410;
const int mod=1e9+7;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int cnt;
int n,m,t;
char S[maxn],T[maxn];
int dp[maxn][maxn][maxn];
int main()
{
t=read();
while(t--)
{
cnt=0;
n=read();
m=read();
scanf("%s",S+1);
scanf("%s",T+1);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
if(S[i]=='-') cnt++;
}
if(n-(cnt*2)!=m)
{
cout<<0<<endl;
continue;
}
int len=0;
dp[0][0][0]=1;
for(int i=1;i<=n;i++)
{
if(S[i]=='-')
{
len--;
}
else
{
len++;
}
for(int j=0;j<=cnt;j++)
{
for(int k=0;k<=cnt;k++)
{
if(S[i]=='-') dp[i][j][k]=(dp[i-1][j+1][k]+dp[i-1][j][k+1])%mod;
else
{
if(k>0) dp[i][j][k]=(dp[i-1][j][k-1])%mod;
else if(T[len-j]==S[i]) dp[i][j][k]=dp[i-1][j][k];
else if(j==len) dp[i][j][k]=(dp[i-1][j][k]+dp[i-1][j-1][k])%mod;
}
}
}
}
cout<<dp[n][0][0]<<endl;
}
return 0;
}
提高组
T1 【NOI Online 2022】丹钓战
听名字就知道是单调栈。。。
我们先模拟一遍题目要求,把每一个点是被哪个点弹出来的记录下来
然后在每个询问区间里一直跳,直到跳出这个区间
但是会T掉(会被卡的)
所以要用倍增优化
依旧是记录的被谁弹出去,然后把这个点弹出去的点又是被谁弹出去的,以此类推就OK啦!
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=5e5+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int n,q;
int a[maxn];
int b[maxn];
int f[maxn][31];
stack <int> s;
int main()
{
n=read();
q=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
}
for(int i=1;i<=n;i++)
{
b[i]=read();
}
for(int i=1;i<=n;i++)
{
f[i][0]=n+1;
}
for(int i=1;i<=n;i++)
{
while(!s.empty() && (a[i]==a[s.top()] || b[i]>=b[s.top()]))
{
f[s.top()][0]=i;
s.pop();
}
s.push(i);
}
for(int j=1;j<=20;j++)
{
for(int i=1;i<=n;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
}
}
for(int tim=1;tim<=q;tim++)
{
int l=read();
int r=read();
int ans=1; //第一组进来的一定满足啦QwQ
for(int j=20;j>=0;j--)
{
if(f[l][j] && f[l][j]<=r)
{
l=f[l][j];
ans+=(1<<j);
}
}
cout<<ans<<endl;
}
return 0;
}
T2 【NOI Online 2022】讨论
感觉挺难得诶,暴力就是对会一道题的人先按k排序然后比较相邻的(正确性可见)
x,y,z一个比一个会的多,那x、y不行,x包含于y,y、z不行,y包含于z,那x包含于y包含于z,很明显x、z也是不行滴
优化嘞?先按会的题的数目从小到大sort 枚举时每次找上一个会这道题得人比较他和现在的这个人是否可行
还是会T的啦
我们可以发现的是遍历上一个人时还能顺便求出来这个人与上一个人的交集
要是交集等于上一个人的会的题的数量就是不行滴
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=1e6+5;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int t,n;
int k[maxn];
int id[maxn];
int cnt[maxn];
int las[maxn]; //这是上一个会这道题的人
vector <int> p[maxn];
bool cmp(int x,int y)
{
return k[x]<k[y];
}
void clear(vector <int> x)
{
vector <int> mid;
mid.swap(x);
}
void work()
{
memset(cnt,0,sizeof(cnt)); //论初始化数组的重要性
memset(las,0,sizeof(las));
n=read();
for(int i=1;i<=n;i++)
{
k[i]=read();
clear(p[i]);
p[i].resize(k[i]);
for(int j=0;j<k[i];j++)
{
p[i][j]=read();
}
id[i]=i;
}
sort(id+1,id+n+1,cmp); //按元素个数从小到大sort
for(int i=1;i<=n;i++)
{
int order=id[i];
if(k[order]==0) continue; //这这这啥也不会肯定被别人包含
for(int j=0;j<k[order];j++) cnt[las[p[order][j]]]++;
//上一个会这道题的人与现在这个人的交集的题的数目++
for(int j=0;j<k[order];j++)
{
int g=las[p[order][j]]; //上一个会这道题的人
if(g && cnt[g]<k[g] && cnt[g]<k[order]) //这个是比较交集啦
{
cout<<"YES"<<endl;
cout<<order<<" "<<g<<endl;
return ;
}
}
for(int j=0;j<k[order];j++)
{
cnt[las[p[order][j]]]--;
las[p[order][j]]=order; //更新一下上一个会这道题的人
}
}
cout<<"NO"<<endl;
}
signed main()
{
t=read();
while(t--)
{
work();
}
return 0;
}
T3 【NOI Online 2022】如何正确地排序
m<=3时,不难得出可以用二维偏序求解,但当m==4时,就会变成三维偏序(我不会写)
正难则反,很明显的,假设所有数对都有贡献,\(ans=2n \times sum\) (即当m==2时的情况)
那我们可以用这个ans减去不满足情况的数对贡献,设三个数组A,B,C
对于B中应满足\(A_i + A_j \leq B_i + B_j\) 且 \(B_i + B_j \leq C_i + C_j\)
移项就是二维偏序力
我们可以让所有情况都变为m==4
枚举任意三个有序数组减去不合法的情况就OK力
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=2e5+5;
const int base=2e5+10;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
f=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int m,n,ans;
int a[5][maxn];
int tree[base*2];
struct que
{
int op,x,y,val;
}q[maxn*2];
int lowbit(int x)
{
return x&-x;
}
void update(int x)
{
for(;x<base*2;x+=lowbit(x)) tree[x]++;
}
int query(int x)
{
int ans=0;
for(;x;x-=lowbit(x)) ans+=tree[x];
return ans;
}
bool cmp(que a,que b)
{
if(a.x!=b.x)
{
return a.x<b.x;
}
return a.op<b.op;
}
int Calc(int x,int y,int z)
{
int sum=0,cnt=0;
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;i++)
{
q[++cnt]=(que){0ll,a[x][i]-a[y][i]+(x>y),a[y][i]-a[z][i]+(y>z),0ll};
q[++cnt]=(que){1ll,a[y][i]-a[x][i],a[z][i]-a[y][i],a[y][i]};
}
sort(q+1,q+cnt+1,cmp);
for(int i=1;i<=cnt;i++)
{
if(q[i].op==0) update(q[i].y+base);
else sum+=q[i].val*query(q[i].y+base);
}
return sum;
}
signed main()
{
m=read();
n=read();
for(int i=0;i<m;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j]=read();
}
}
for(int i=m;i<4;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j]=a[i-m][j];
}
}
int ans=0;
for(int i=0;i<4;i++)
{
for(int j=1;j<=n;j++)
{
ans+=2*n*a[i][j];
}
}
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
for(int k=0;k<4;k++)
if(i!=j && j!=k && i!=k) ans-=Calc(i,j,k);
cout<<ans;
return 0;
}