20240814
赛时得分
| 题目 | A | B | C | D | 总分 | 排名 | 比例 |
|---|---|---|---|---|---|---|---|
| 满分 | 100 | 100 | 100 | 100 | 400 | 160 | 100% |
| 得分 | 50 | 30 | 0 | 20 | 100 | 93 | 58.1% |
A. 二进制的数字(50/100)
\(\text{50%}\) 得分做法,十进制转二进制后直接从 \(1\) 开始枚举,如果满足 \(k\) 个 \(1\),我们就 ans++,直到 ans=n 结束。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
ll n,k,cnt,ans;
string now;
string change(ll n)
{
if(n==0) return "0";
string ans;
while(n!=1)
{
ans+=to_string(n%2);
n/=2;
}
ans+="1";
reverse(ans.begin(),ans.end());
return ans;
}
int main()
{
freopen("num.in","r",stdin);
freopen("num.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k;
for(int i=1;;i++)
{
cnt=0;
now=change(i);
for(int j=0;j<now.length();j++)
{
if(now[j]=='1') cnt++;
}
if(cnt!=k) continue;
else ans++;
if(ans==n)
{
cout<<now;
return 0;
}
}
return 0;
}
B. 插入加号(30/100)
\(\text{30%}\) 得分做法,考虑 dfs,我们插入加号的位置一定在两个数之间,就搜索加号放的位置,扔到一个 set 中维护,当元素个数 \(=k\) 时,我们就需要求一下和即可。
还好昨晚鬼使神差看了 Luogu P1036 选数 这个题,跟 B 题的 dfs 思路是很像的。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int mod=1e9+7;
ll n,k,ans;
string tar;
set<ll> pl;
ll getnum(string s)
{
ll ans=0,res=0;
for(int i=0;i<s.length();i++)
{
if(s[i]=='+')
{
ans+=res%mod;
res=0;
}
else
{
res*=10;
string now="";
now+=s[i];
res+=stoi(now);
res%=mod;
}
}
ans+=res%mod;
return ans;
}
string makestr(set<ll> pl,string s)
{
string ans="";
ans+=s[0];
for(int i=1;i<n;i++)
{
if(pl.find(i)!=pl.end()) ans+="+",ans+=s[i];
else ans+=s[i];
}
return ans;
}
void dfs(ll now)
{
if(now>n) return;
if(now==n)
{
if(pl.size()==k)
{
ans+=getnum(makestr(pl,tar));
ans%=mod;
}
}
pl.insert(now);
dfs(now+1);
pl.erase(pl.find(now));
dfs(now+1);
return;
}
int main()
{
freopen("plus.in","r",stdin);
freopen("plus.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>k>>tar;
dfs(1);
cout<<ans;
return 0;
}
C. 疯狂的动态树(40/100)
\(\text{20%}\) 得分做法,考虑暴力,我们写三个 dfs,分别用于换根、路径查询、子树查询。
- 对于换根 dfs,我们在这里统计 \(d,fat\) 数组,分别记录的是深度和父节点。
- 对于路径查询 dfs,我们直接 dfs 到终点,传参记录答案即可。
- 对于子树查询 dfs,由于树上 dfs 不会走重复的路,我们每到一个点直接记录答案即可。记得我们搜索的是子树,子树根节点 \(x\) 父亲不是 \(0\),而是 \(fat_x\)。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=3e5+1;
vector<ll> e[N];
ll id,n,m,op,u,v,d[N],fat[N],ans;
void dfs1(ll x,ll fa)
{
d[x]=d[fa]+1;
fat[x]=fa;
for(auto y:e[x])
{
if(y!=fa) dfs1(y,x);
}
return;
}
void dfs2(ll x,ll fa,ll now)
{
if(x==v) ans=now;
for(auto y:e[x])
{
if(y!=fa) dfs2(y,x,now+(d[y]-1));
}
return;
}
void dfs3(ll x,ll fa)
{
ans+=(d[x]-1);
for(auto y:e[x])
{
if(y!=fa) dfs3(y,x);
}
return;
}
int main()
{
freopen("crazy.in","r",stdin);
freopen("crazy.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>id>>n>>m;
for(int i=1;i<n;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,0);
while(m--)
{
cin>>op;
if(op==0)
{
cin>>u;
memset(d,0,sizeof(d));
memset(fat,0,sizeof(fat));
dfs1(u,0);
}
else if(op==1)
{
cin>>u>>v;
ans=0;
dfs2(u,0,d[u]-1);
cout<<ans<<endl;
}
else
{
cin>>u;
ans=0;
dfs3(u,fat[u]);
cout<<ans<<endl;
}
}
return 0;
}
\(\text{40%}\) 得分做法,这个做法是没有换根操作的部分分。
对于路径查询操作,不难发现深度上答案即为 \(u\) 到根节点的深度总和 + \(v\) 到根节点的深度总和 - \(lca(u,v)\) 到根节点的深度总和。化简就是 \(\sum^{d_u}_{i=d_{lca(u,v)}} + \sum^{d_v}_{i=d_{lca(u,v)}}-\ d_{lca(u,v)}\) 我们只需要等差数列维护一下即可。
对于子树查询操作,我们提前 dfs 预处理一下以每个节点为根节点的子树深度和是多少即可,思想类似于前缀和。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=3e5+1;
vector<ll> e[N];
ll id,n,m,op,u,v,d[N],fat[N],ans,dep[N],p[N][21],sum[N];
void dfs(ll x,ll fa)
{
d[x]=d[fa]+1;
p[x][0]=fa;
fat[x]=fa;
sum[x]=d[x]-1;
for(int i=1;(1<<i)<=d[x];i++) p[x][i]=p[p[x][i-1]][i-1];
for(auto y:e[x])
{
if(y!=fa)
{
dfs(y,x);
sum[x]+=sum[y];
}
}
return;
}
ll lca(ll a,ll b)
{
if(d[a]>d[b]) swap(a,b);
for(int i=20;i>=0;i--)
{
if(d[a]<=d[b]-(1<<i)) b=p[b][i];
}
if(a==b) return a;
for(int i=20;i>=0;i--)
{
if(p[a][i]==p[b][i]) continue;
else a=p[a][i],b=p[b][i];
}
return p[a][0];
}
int main()
{
freopen("crazy.in","r",stdin);
freopen("crazy.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>id>>n>>m;
for(int i=1;i<n;i++)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0);
while(m--)
{
cin>>op>>u;
if(op==0)
{
memset(d,0,sizeof(d));
memset(p,0,sizeof(p));
memset(fat,0,sizeof(fat));
memset(sum,0,sizeof(sum));
dfs(u,0);
}
else if(op==1)
{
cin>>v;
ll st=d[lca(u,v)]-1,ed1=d[u]-1,ed2=d[v]-1;
ans=0;
ans+=(st+ed1)*(ed1-st+1)>>1;
ans+=(st+ed2)*(ed2-st+1)>>1;
ans-=st;
cout<<ans<<endl;
}
else cout<<sum[u]<<endl;
}
return 0;
}
D. 聪明格(20/100)
\(\text{20%}\) 得分做法,看到 \(n=3\) 的部分分,直接打表。一共有 \(12\) 种数独方案,我们需要统计一下乘积数组中各元素对应坐标,然后拿到我们的 \(12\) 个方案中一一比较,满足方案的我们就方案数 ++,把字典序最小那个编号存下来,最后输出即可。
但这样策略显然有反例。例如我出一个乘积数组如下:
6 6 3
6 2 6
1 6 6
由于此数组非联通,而我统计出来的结果是有 \(6\) 个 \(6\),所以我需要特判一下这种情况,还有把它瞬时针旋转 \(90\) 度(放到另两个对角)的情况即可。
可能还有反例吧。但是我懒得找了。而且出题人也没有造特殊数据卡这种做法,所以就水来 \(\text{20pts}\)。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=21;
ll n,mul[N][N];
struct lhm
{
ll a[N][N];
}s[N];
void pre()
{
s[1].a[1][1]=1;s[1].a[1][2]=2;s[1].a[1][3]=3;
s[1].a[2][1]=2;s[1].a[2][2]=3;s[1].a[2][3]=1;
s[1].a[3][1]=3;s[1].a[3][2]=1;s[1].a[3][3]=2;
s[2].a[1][1]=1;s[2].a[1][2]=2;s[2].a[1][3]=3;
s[2].a[2][1]=3;s[2].a[2][2]=1;s[2].a[2][3]=2;
s[2].a[3][1]=2;s[2].a[3][2]=3;s[2].a[3][3]=1;
s[3].a[1][1]=1;s[3].a[1][2]=3;s[3].a[1][3]=2;
s[3].a[2][1]=2;s[3].a[2][2]=1;s[3].a[2][3]=3;
s[3].a[3][1]=3;s[3].a[3][2]=2;s[3].a[3][3]=1;
s[4].a[1][1]=1;s[4].a[1][2]=3;s[4].a[1][3]=2;
s[4].a[2][1]=3;s[4].a[2][2]=2;s[4].a[2][3]=1;
s[4].a[3][1]=2;s[4].a[3][2]=1;s[4].a[3][3]=3;
s[5].a[1][1]=2;s[5].a[1][2]=1;s[5].a[1][3]=3;
s[5].a[2][1]=1;s[5].a[2][2]=3;s[5].a[2][3]=2;
s[5].a[3][1]=3;s[5].a[3][2]=2;s[5].a[3][3]=1;
s[6].a[1][1]=2;s[6].a[1][2]=1;s[6].a[1][3]=3;
s[6].a[2][1]=3;s[6].a[2][2]=2;s[6].a[2][3]=1;
s[6].a[3][1]=1;s[6].a[3][2]=3;s[6].a[3][3]=2;
s[7].a[1][1]=2;s[7].a[1][2]=3;s[7].a[1][3]=1;
s[7].a[2][1]=1;s[7].a[2][2]=2;s[7].a[2][3]=3;
s[7].a[3][1]=3;s[7].a[3][2]=1;s[7].a[3][3]=2;
s[8].a[1][1]=2;s[8].a[1][2]=3;s[8].a[1][3]=1;
s[8].a[2][1]=3;s[8].a[2][2]=1;s[8].a[2][3]=2;
s[8].a[3][1]=1;s[8].a[3][2]=2;s[8].a[3][3]=3;
s[9].a[1][1]=3;s[9].a[1][2]=1;s[9].a[1][3]=2;
s[9].a[2][1]=1;s[9].a[2][2]=2;s[9].a[2][3]=3;
s[9].a[3][1]=2;s[9].a[3][2]=3;s[9].a[3][3]=1;
s[10].a[1][1]=3;s[10].a[1][2]=1;s[10].a[1][3]=2;
s[10].a[2][1]=2;s[10].a[2][2]=3;s[10].a[2][3]=1;
s[10].a[3][1]=1;s[10].a[3][2]=2;s[10].a[3][3]=3;
s[11].a[1][1]=3;s[11].a[1][2]=2;s[11].a[1][3]=1;
s[11].a[2][1]=1;s[11].a[2][2]=3;s[11].a[2][3]=2;
s[11].a[3][1]=2;s[11].a[3][2]=1;s[11].a[3][3]=3;
s[12].a[1][1]=3;s[12].a[1][2]=2;s[12].a[1][3]=1;
s[12].a[2][1]=2;s[12].a[2][2]=1;s[12].a[2][3]=3;
s[12].a[3][1]=1;s[12].a[3][2]=3;s[12].a[3][3]=2;
return;
}
bool pd(int k,set<ll> p[])
{
for(int i=1;i<=250;i++)
{
if(p[i].size()==0) continue;
else
{
if(i==6 and p[i].size()==6)
{
ll cnt1=1,cnt2=1;
bool vis3=0,vis1=0;
for(auto j:p[i])
{
if(j==1)
{
vis1=1;
break;
}
if(j==3)
{
vis3=1;
break;
}
}
if(vis1==1)
{
cnt1*=s[k].a[1][1];
cnt1*=s[k].a[1][2];
cnt1*=s[k].a[2][1];
cnt2*=s[k].a[2][3];
cnt2*=s[k].a[3][2];
cnt2*=s[k].a[3][3];
if(cnt1!=6 or cnt2!=6) return 0;
}
if(vis3==1)
{
cnt1*=s[k].a[1][3];
cnt1*=s[k].a[1][2];
cnt1*=s[k].a[2][3];
cnt2*=s[k].a[2][1];
cnt2*=s[k].a[3][2];
cnt2*=s[k].a[3][1];
if(cnt1!=6 or cnt2!=6) return 0;
}
}
else
{
ll cnt=1;
for(auto j:p[i])
{
if(j==3 or j==6 or j==9) cnt*=s[k].a[j/n][3];
else cnt*=s[k].a[j/n+1][j%n];
}
if(cnt!=i) return 0;
}
}
}
return 1;
}
void sub()
{
pre();
ll ans=0,pos=0;
set<ll> p[301];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>mul[i][j];
p[mul[i][j]].insert(n*(i-1)+j);
}
}
for(int k=1;k<=12;k++)
{
if(pd(k,p)==1)
{
ans++;
if(ans==1) pos=k;
}
}
cout<<ans<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) cout<<s[pos].a[i][j]<<" ";
cout<<endl;
}
return;
}
int main()
{
freopen("smart.in","r",stdin);
freopen("smart.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
if(n==3)
{
sub();
return 0;
}
return 0;
}

浙公网安备 33010602011771号