NOI 2023 春季测试
前言
小弱鸡在暑假时候闲的没事尝试打打NOI春季,总分是255分……(有点特殊的含义)
正文
T1:[春季测试 2023] 涂色游戏
非常简单的一道普及题,针对每个操作记录行和列上最近的更新。
在输出的时候查询一下即可。
#include<bits/stdc++.h>
using namespace std;
int T;
int n,m,q;
struct opt{
int loc, tim;
}qur_1[100005], qur_2[100005];
int main()
{
// freopen("paint2.in","r",stdin);
// freopen("paint.out","w",stdout);
cin>>T;
while(T){
T--;
memset(qur_1,0,sizeof qur_1);
memset(qur_2,0,sizeof qur_2);
cin>>n>>m>>q;
for(int i=1;i<=q;i++){
int op, lo, col;
cin>>op>>lo>>col;
if(op == 0){
qur_1[lo].loc=col;
qur_1[lo].tim=i;
}else{
qur_2[lo].loc=col;
qur_2[lo].tim=i;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int color;
if(qur_1[i].tim > qur_2[j].tim){
color=qur_1[i].loc;
}else{
color=qur_2[j].loc;
}
if(j!=m) cout<<color<<' ';
else cout<<color;
}
cout<<endl;
}
}
return 0;
}
T2: [春季测试 2023] 幂次
这道题有一点考数论了。
针对1e12以下的数据,我们还可以以接近\(O(\sqrt n)\)的复杂度进行枚举。
但是对于1e18的数据规模,在\(k = 3\)时还可以勉强过关,但\(k = 2\)时就会GG。
所以要先计算\(k = 2\)时的情况为至少\(\sqrt n\),然后再特判\(k = 3\)的情况会不会增加新数。
对于新数的条件,是底数不为完全平方数,且指数为奇数,然后这个数还没有出现过,可以用set来去重。
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
ll n;
int k;
ll ans;
ll f[100000005];
set<ll> tmp;
map<ll,ll> atp;
ll pre[10000005];
int main()
{
// freopen("power4.in","r",stdin);
//cout<<pow(99, (double)1.0/6.0);
cin>>n>>k;
if(k==1){
cout<<n<<endl;
return 0;
}else if(n<=1e6){
for(int i=1;pow(i,k)<=n;i++){
if(i==1) f[1]=1;
else{
int cnt=k;
while(1){
int hk=pow(i,cnt);
if(hk>n) break;
if(hk<=n){
f[hk]=1;
}
cnt++;
}
}
}
for(int i=1;i<=n;i++){
if(f[i]){
ans++;
}
}
cout<<ans;
}else if(k >= 3){
for(ll i=1;pow(i,k)<=n;i++){
if(i==1){
tmp.insert(1);
}else{
for(int j=k;pow(i,j)<=n;j++){
tmp.insert(pow(i,j));
}
}
}
cout<<tmp.size()<<endl;
}else if(k == 2){
ll ans = sqrtl(n);
k=3;
for(int i=1;i<=1000;i++){
pre[i*i]=1;
}
for(ll i=2;pow(i,k)<=n;i++){
if(tmp.find(i) != tmp.end() || pre[i]) continue;
for(ll j=k;pow(i,j)<=n;j++){
if(tmp.find(pow(i,j)) == tmp.end() && j%2 == 1) ans++;
tmp.insert(pow(i,j));
}
}
cout<<ans<<endl;
}
return 0;
}
T3: [春季测试 2023] 圣诞树
区间DP,判断从序列的左侧还是右侧添加线段。
注意输入数据保证凸多边形,但是要求是从最高点开始输出,所以开始时要旋转一下,确定最后一个点为起始点。
最后还要判断一下,起始点的位置。
\[f[l][r][0] = \min(f[l+1][r][0] + dis(l,l+1), f[l+1][r][1] + dis(l,r))
\]
\[f[l][r][1] = \min(f[l][r-1][0] + dis(l,r), f[l+1][r][1] + dis(r-1,r))
\]
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
struct node{double x,y;int id;}a[maxn],tmp[maxn];
double f[maxn][maxn][2];
int pre[maxn][maxn][2];
double dis(int i,int j){return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y));}
void print(int l,int r,int op)//二分输出
{
if(l==r) return cout<<a[l].id<<' ',void();
if(op) cout<<a[r].id<<' ',print(l,r-1,pre[l][r][op]);
else cout<<a[l].id<<' ',print(l+1,r,pre[l][r][op]);
}
int main()
{
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y,a[i].id=i,tmp[i]=a[i];
int k=1;
for(int i=2;i<=n;i++) if(a[i].y>a[k].y) k=i;
for(int i=1;i<=k;i++) a[i+n-k]=tmp[i];
for(int i=k+1;i<=n;i++) a[i-k]=tmp[i];//题目保证形成凸多边形,所以要旋转
// swap(a[n],a[k]);
for(int len=2;len<n;len++)//区间DP,从段长开始
for(int i=1,j=len;j<n;i++,j++)//左右端点
{
f[i][j][0]=f[i][j][1]=1e18;//初始化
if(f[i][j][0]>f[i+1][j][0]+dis(i,i+1)) f[i][j][0]=f[i+1][j][0]+dis(i,i+1),pre[i][j][0]=0;//四种情况
if(f[i][j][0]>f[i+1][j][1]+dis(i,j)) f[i][j][0]=f[i+1][j][1]+dis(i,j),pre[i][j][0]=1;
if(f[i][j][1]>f[i][j-1][0]+dis(j,i)) f[i][j][1]=f[i][j-1][0]+dis(i,j),pre[i][j][1]=0;
if(f[i][j][1]>f[i][j-1][1]+dis(j,j-1)) f[i][j][1]=f[i][j-1][1]+dis(j,j-1),pre[i][j][1]=1;
}
cout<<a[n].id<<' ';//最高点
if(f[1][n-1][0]+dis(1,n)>f[1][n-1][1]+dis(n-1,n)) print(1,n-1,1);//最后是右还是左,经过所有顶点恰好一次
else print(1,n-1,0);
return 0;
}