CSP-S2023题解
考场上只做出来两题,赛后发现好像都不难
T1:
shaber题,爆搜就行了
考场代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[15][15];
int b[15];
int ans;
bool check()
{
for(int i=1;i<=n;i++)
{
int las=0;
for(int j=1;j<=5;j++)
{
if(b[j]!=a[i][j])
{
if(las==-1)return false;
if(las)
{
int p1=0,p2=0,q1=0,q2=0;
if(las!=j-1)return false;
if(b[j]<a[i][j])p1=b[j]+10-a[i][j],p2=a[i][j]-b[j];
else p1=b[j]-a[i][j],p2=a[i][j]+10-b[j];
if(b[j-1]<a[i][las])q1=b[j-1]+10-a[i][las],q2=a[i][las]-b[j-1];
else q1=b[j-1]-a[i][las],q2=a[i][las]+10-b[j-1];
las=-1;
if(p1!=q1 && p2!=q2)return false;
}
else las=j;
}
}
if(las==0)return false;
}
return true;
}
void dfs(int i)
{
if(i==6)
{
if(check())ans++;
return ;
}
for(int j=1;j<=9;j++)
{
b[i]=j;
dfs(i+1);
}
b[i]=0;
dfs(i+1);
}
int main()
{
// freopen("lock.in","r",stdin);
// freopen("lock.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=5;j++)cin>>a[i][j];
}
dfs(1);
cout<<ans;
return 0;
}
T2:
很容易想到一个\(n^2\) 的做法,钦定$i $为左端点,枚举右端点,用一个栈判断区间是否合法。
考虑优化这个做法,首先注意到我们只需要找到以\(i\) 为左端点最小的合法区间就行,用\(f[i]\) 表示以\(i\) 为左端点,有多少个合法区间,设\(j\) 为以\(i\) 为左端点,\([i,j]\) 是最小的合法区间,因为合法区间可以是由多个合法区间合起来的,所以以\(i\) 为左端点的合法区间肯定是由\([i,j],[j+1,?]\) 组合起来的,所以\(f[i]=f[j+1]+1\) 。
那么怎么找以\(i\) 为左端点最小的合法区间呢,显然如果这个区间是个最小的合法区间,那么\(s[j]\)肯定与\(s[i]\) 相等,且\([i+1,j-1]\) 为合法区间,考虑记录每个点以自己为左端点的最小合法区间右端点的位置,只需要从i+1一直跳,直到跳到一个字母与$s[i] $相等的就结束,这个位置就是最小的合法区间的右端点。
时间复杂度大概是\(26n\) 吧,不会证明,但是CCF的数据跑得挺快的,最大的点也只有\(80ms\)
考场代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int n;
long long f[maxn],ans;
int r[maxn];
string s;
int main()
{
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
cin>>n;
cin>>s;
for(int i=n-1;i>=0;i--)
{
r[i]=n;
int j=i+1;
while(j<n)
{
if(s[j]==s[i])
{
r[i]=j+1;
f[i]+=f[j+1]+1;
break;
}
j=r[j];
}
ans+=f[i];
}
cout<<ans;
return 0;
}
T3:
这题考场上没做出来,看题目太长了就去做T2了,赛后2.5h打完了,还是很后悔的。
前期工作:
类型:对每个类型建一个结构体
struct LX
{
int nc,dj;//大小,对齐要求
int cys;//成员数
st cy[105];//成员都是啥
string na;//名字
vector<int >s;//成员的起始位置
map<string,int>ys;//某个字符串是第几个成员
}c[1005];
元素:对每个元素建一个结构体
struct st
{
int l,r,lx;//在内存中的起始位置和结束位置,类型
string na;//名字
}a[1000005];
操作一:
暴力更新一下结构体的各项数据,也不难理解
cin>>c[++cnt1].na>>c[cnt1].cys;
q[c[cnt1].na]=cnt1;
int las1=0;
for(int j=1;j<=c[cnt1].cys;j++)
{
string lx,x;
cin>>lx>>c[cnt1].cy[j].na;
c[cnt1].cy[j].lx=q[lx];
if(las1==0)c[cnt1].cy[j].l=0;
else c[cnt1].cy[j].l=ce(las1,c[c[cnt1].cy[j].lx].dj);
c[cnt1].ys[c[cnt1].cy[j].na]=j;
c[cnt1].cy[j].r=c[cnt1].cy[j].l+c[c[cnt1].cy[j].lx].nc-1;
c[cnt1].dj=max(c[cnt1].dj,c[c[cnt1].cy[j].lx].dj);
las1=c[cnt1].cy[j].r+1;
c[cnt1].s.push_back(j);
}
c[cnt1].nc=ce(c[cnt1].cy[c[cnt1].cys].r+1,c[cnt1].dj);
cout<<c[cnt1].nc<<" "<<c[cnt1].dj<<endl;
操作2:
和操作1差不多,把这个变量看做是0号类型的成员
string lx,x;
cin>>lx>>c[0].cy[++cnt].na;
c[0].cys++;
c[0].cy[cnt].lx=q[lx];
if(las==0)
{
c[0].cy[cnt].l=0;
}
else c[0].cy[cnt].l=ce(las,c[c[0].cy[cnt].lx].dj);
c[0].ys[c[0].cy[cnt].na]=cnt;
c[0].cy[cnt].r=c[0].cy[cnt].l+c[c[0].cy[cnt].lx].nc-1;
c[0].dj=max(c[0].dj,c[c[0].cy[cnt].lx].dj);
las=c[0].cy[cnt].r+1;
c[0].s.push_back(cnt);
cout<<c[0].cy[cnt].l<<endl;
操作3:
用一个函数\(fi(i,s)\) 表示在i号结构体中,元素s的起始位置,一层一层的去找就行了。
int fi(int lx,string nam)
{
int k=nam.find(".");
if(k!=-1)
{
string nam1=nam.substr(0,k);
nam.erase(0,k+1);
return c[lx].cy[c[lx].ys[nam1]].l+fi(c[lx].cy[c[lx].ys[nam1]].lx,nam);
}
return c[lx].cy[c[lx].ys[nam]].l;
}
操作4:
用函数$fin(i,x) $表示在i号结构体中x内存对应的元素,和操作3差不多,一层一层的找,用二分找出第一个起始位置大于x位置的元素,然后看一下这个元素的结束位置是否小于x,小于x说明x位置是空的,返回ERR,不然就正常返回,如果这个元素没有成员了,说明他是基本类型,返回空。
这里要注意的是要特判一下0号结构体成员为空的情况
string fin(int lx,int x)
{
int l=1,r=c[lx].cys;
while(l<=r)
{
int mid=(l+r)>>1;
if(c[lx].cy[mid].l>x)r=mid-1;
else l=mid+1;
}
if(c[lx].cys==0)
{
if(lx==0)return "ERR";
return "";
}
string k=fin(c[lx].cy[r].lx,x-c[lx].cy[r].l);
if(k=="")
{
if(c[lx].cy[r].r<x)
{
return "ERR";
}
return c[lx].cy[r].na;
}
if(k=="ERR")return "ERR";
return c[lx].cy[r].na+"."+k;
}
最后再把这4种操作结合起来,我们就成功做出这道毒瘤大模拟了。
完整代码:
#include<bits/stdc++.h>
#define int long long
#define ce(a,b) (((a-1)/b+1)*b)
using namespace std;
int n,op;
int las=0;
vector<pair <int ,int > >f;
int cnt;
struct st
{
int l,r,lx;
string na;
}a[1000005];
int cnt1;
struct LX
{
int nc,dj;
int cys;
st cy[105];
string na;
vector<int >s;
map<string,int>ys;
}c[1005];
map<string,int>q;
int fi(int lx,string nam)
{
int k=nam.find(".");
if(k!=-1)
{
string nam1=nam.substr(0,k);
nam.erase(0,k+1);
return c[lx].cy[c[lx].ys[nam1]].l+fi(c[lx].cy[c[lx].ys[nam1]].lx,nam);
}
return c[lx].cy[c[lx].ys[nam]].l;
}
string fin(int lx,int x)
{
int l=1,r=c[lx].cys;
while(l<=r)
{
int mid=(l+r)>>1;
if(c[lx].cy[mid].l>x)r=mid-1;
else l=mid+1;
}
if(c[lx].cys==0)
{
if(lx==0)return "ERR";
return "";
}
string k=fin(c[lx].cy[r].lx,x-c[lx].cy[r].l);
if(k=="")
{
if(c[lx].cy[r].r<x)
{
return "ERR";
}
return c[lx].cy[r].na;
}
if(k=="ERR")return "ERR";
return c[lx].cy[r].na+"."+k;
}
signed main()
{
cin>>n;
q["byte"]=1;
q["short"]=2;
q["int"]=3;
q["long"]=4;
c[1].nc=1;
c[2].nc=2;
c[3].nc=4;
c[4].nc=8;
c[1].dj=1;
c[2].dj=2;
c[3].dj=4;
c[4].dj=8;
cnt1=4;
for(int i=1;i<=n;i++)
{
cin>>op;
if(op==1)
{
cin>>c[++cnt1].na>>c[cnt1].cys;
q[c[cnt1].na]=cnt1;
int las1=0;
for(int j=1;j<=c[cnt1].cys;j++)
{
string lx,x;
cin>>lx>>c[cnt1].cy[j].na;
c[cnt1].cy[j].lx=q[lx];
if(las1==0)c[cnt1].cy[j].l=0;
else c[cnt1].cy[j].l=ce(las1,c[c[cnt1].cy[j].lx].dj);
c[cnt1].ys[c[cnt1].cy[j].na]=j;
c[cnt1].cy[j].r=c[cnt1].cy[j].l+c[c[cnt1].cy[j].lx].nc-1;
c[cnt1].dj=max(c[cnt1].dj,c[c[cnt1].cy[j].lx].dj);
las1=c[cnt1].cy[j].r+1;
c[cnt1].s.push_back(j);
}
c[cnt1].nc=ce(c[cnt1].cy[c[cnt1].cys].r+1,c[cnt1].dj);
cout<<c[cnt1].nc<<" "<<c[cnt1].dj<<endl;
}
else if(op==2)
{
string lx,x;
cin>>lx>>c[0].cy[++cnt].na;
c[0].cys++;
c[0].cy[cnt].lx=q[lx];
if(las==0)
{
c[0].cy[cnt].l=0;
}
else c[0].cy[cnt].l=ce(las,c[c[0].cy[cnt].lx].dj);
c[0].ys[c[0].cy[cnt].na]=cnt;
c[0].cy[cnt].r=c[0].cy[cnt].l+c[c[0].cy[cnt].lx].nc-1;
c[0].dj=max(c[0].dj,c[c[0].cy[cnt].lx].dj);
las=c[0].cy[cnt].r+1;
c[0].s.push_back(cnt);
cout<<c[0].cy[cnt].l<<endl;
}
else if(op==3)
{
string s;
cin>>s;
cout<<fi(0,s)<<endl;
}
else if(op==4)
{
int x;
cin>>x;
cout<<fin(0,x)<<endl;
}
}
return 0;
}
T4:
很明显答案具有单调性,于是尝试用二分解决这道题,我们处理出每个点最迟要在什么时候种。然后贪心的先种时间早的树,从往上找直到找到根节点或者一个已经种了的树,然后一直种下来,最后判断一下所有树能不能长成就行了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
int n,a[maxn],c[maxn],b[maxn];
int f[maxn],fa[maxn],maxx;
int d[maxn],e[maxn];
vector<int>v[maxn];
int x=1;
inline void getf(int u)
{
if(f[u])return ;
getf(fa[u]);
f[u]=++x;
}
inline void dfs(int u,int fath)
{
fa[u]=fath;
for(int i=0;i<v[u].size();i++)
{
if(v[u][i]==fath)continue;
dfs(v[u][i],u);
}
}
int p[maxn];
bool cmp(int x,int y)
{
return e[x]<e[y];
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",a+i,b+i,c+i);
}
for(int i=1;i<n;i++)
{
int u,y;
scanf("%lld%lld",&u,&y);
v[u].push_back(y);
v[y].push_back(u);
}
dfs(1,1);
int l=n+1,r=1e9;
while(l<=r)
{
int mid=(l+r)>>1;
int cnt=0;
for(int i=1;i<=n;i++)
{
if(c[i]<0)
{
int o=min((b[i]-1)/-c[i],mid);
int L=1,R=mid;
while(L<=R)
{
int Mid=(L+R)>>1;
__int128 sum=0;
if(Mid<=o)
{
sum=b[i]+b[i];
sum+=o*c[i];
sum+=Mid*c[i];
sum*=(o-Mid+1);
sum/=2;
sum+=mid-o;
}
else sum+=mid-Mid+1;
if(sum>=a[i])L=Mid+1;
else R=Mid-1;
}
e[i]=R;
}
else
{
int L=1,R=mid;
while(L<=R)
{
int Mid=(L+R)>>1;
__int128 sum=0;
sum=Mid+mid;
sum*=c[i];
sum+=b[i]+b[i];
sum*=(mid-Mid+1);
sum/=2;
if(sum>=a[i])L=Mid+1;
else R=Mid-1;
}
e[i]=R;
}
if(e[i]<n)p[++cnt]=i;
}
f[1]=1;
sort(p+1,p+cnt+1,cmp);
x=1;
for(int i=1;i<=cnt;i++)
{
getf(p[i]);
}
bool o=false;
for(int i=1;i<=n;i++)
{
if(f[i]>e[i])
{
o=true;
}
f[i]=0;
}
if(o)l=mid+1;
else r=mid-1;
}
cout<<l;
return 0;
}

浙公网安备 33010602011771号