目录
L3-2 关于深度优先搜索和逆序对的题应该不会很难吧这件事 -分数 30
L1
L1-1 今天我要赢 -分数 5
签到题
代码:
L1-2 种钻石 -分数 5
代码:
L1-3 谁能进图书馆 -分数 10
比较麻烦,就是按照题目要求写if
代码:
L1-4 拯救外星人 -分数 10
代码:
L1-5 试试手气 -分数 15
代码:
L1-6 斯德哥尔摩火车上的题 -分数 15
代码:
L1-7 机工士姆斯塔迪奥 -分数 20
由于每次只会涂一行或者一列所以可以发现安全的位置是n*m-(n*涂了一列的次数-m*涂了一行的次数+涂了一列的次数*涂了一行的次数);
代码:
L1-8 静静的推荐 -分数 20
可以先把可以通过pat考试通过的添加到答案,然后由于只进行K轮,每次都取一个严格递增数列,那么只需要对每个大于175分数的人数与K取最小值加到答案即可
代码:
L2
L2-1 插松枝 -分数 25
模拟题 ,根据题目要求模拟即可,每次先从小盒子里取松针,如果没有就从推送器上取......值得注意的是有3个要输出松枝的条件。
代码:
//---------------------------↓乱定义区------------------------------------
int n,m,k;
stack<int>he;
queue<int>tui;
deque<int>zhi;
//---------------------------↓乱写乘区------------------------------------
void solve()
{
cin>>n>>m>>k;
for(int i=1,j;i<=n;i++)
{
cin>>j;
tui.push(j);
}
for(int i=1;i<=n;i++)//一共要插n个,保证一回合插一个即可
{
kai:{}
int f=0;
if(!he.empty() && (zhi.empty() || zhi.back()>=he.top()))
{
f=1;
zhi.push_back(he.top());he.pop();
}
if(!f)
{
if(!tui.empty() && (zhi.empty()|| zhi.back()>=tui.front()))
{
f=1;
zhi.push_back(tui.front());tui.pop();
}else
{
while(!tui.empty() && he.size()<m && zhi.back()<tui.front())
{
he.push(tui.front());tui.pop();
}
if(!tui.empty() && (zhi.empty()|| zhi.back()>=tui.front()))
{
f=1;
zhi.push_back(tui.front());tui.pop();
}
}
}
if(!f || zhi.size()==k ||i==n)
{
int e=0;
while(!zhi.empty())
{
if(!e)e=1;
else cout<<" ";
cout<<zhi.front();
zhi.pop_front();
}
cout<<endl;
}
if(!f)goto kai;//这个回合没有插,那么相当于这个回合没有结束,继续插
}
}
int main()
{
IO;
int tn=1;
//cin>>tn;
while(tn--)
{
solve();
}
return 0;
}
L2-2 老板的作息表 -分数 25
代码:
//---------------------------↓乱定义区------------------------------------
int n,kai=0;
set<pair<int,int>>st;
string s;
//---------------------------↓乱写乘区------------------------------------
void solve()
{
cin>>n;
getchar();
for(int i=0;i<=n;i++)
{
getline(cin,s);
int a,b,c,d,e,f;
sscanf(s.c_str(),"%d:%d:%d - %d:%d:%d",&a,&b,&c,&d,&e,&f);
st.insert({a*3600+b*60+c,d*3600+e*60+f});
}
for(auto [a,b]:st)
{
if(kai<a)printf("%02d:%02d:%02d - %02d:%02d:%02d\n",kai/3600,kai%3600/60,kai%60,a/3600,a%3600/60,a%60);
kai=b;
}
if(kai!=23*3600+59*60+59)
{
printf("%02d:%02d:%02d - %02d:%02d:%02d\n",kai/3600,kai%3600/60,kai%60,23,59,59);
}
}
L2-3 龙龙送外卖 -分数 25
对于节点a,b,如果a,b互不是对方祖先,可以发现,不论先去a还是b,都得在回去他们的最近公共祖先,然后在去往另一个点,所以每条边对答案的贡献都是2,由于送完最后一趟后不用返回,那么只需要减去从根节点到最远需要到达的点的距离即可。我们可以先从根节点dfs求出到所有点的最短距离,然后根据每个需要到达的点,向上走到没有走过的点统计每条边的贡献即可。
代码:
//---------------------------↓乱定义区------------------------------------
int n,m,root;
int dis[maxn];
int fa[maxn],f[maxn];
vector<int>v[maxn];
//---------------------------↓乱写乘区------------------------------------
void dfs(int rt,int len)
{
dis[rt]=len;
for(int i:v[rt])
{
dfs(i,len+1);
}
}
void solve()
{
cin>>n>>m;
for(int i=1,j;i<=n;i++)
{
cin>>j;
fa[i]=j;
if(j==-1)root=i;
else v[j].pd(i);
}
dfs(root,0);
f[root]=1;
int ans=0,ma=0;
for(int i=1,j;i<=m;i++)
{
cin>>j;
ma=max(ma,dis[j]);
while(!f[j])
{
f[j]=1,j=fa[j],ans+=2;
}
cout<<ans-ma<<endl;
}
}
L2-4 大众情人 -分数 25
由于朋友的朋友可以通过中间的朋友传递距离,所以可以看出这是个多源路径最短路问题,直接Floyd,然后根据题目要求,找到答案即可。
代码:
//---------------------------↓乱定义区------------------------------------
int n;
string s;
int k;
int f[maxn];
ll dp[maxn][maxn];
//---------------------------↓乱写乘区------------------------------------
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)dp[i][j]=0xfffffffffffff;
for(int i=1;i<=n;i++)dp[i][i]=0;
for(int i=1;i<=n;i++)
{
cin>>s>>k;
if(s[0]=='F')f[i]=1;
for(int j=0,a,b;j<k;j++)
{
cin>>s;
sscanf(s.c_str(),"%d:%d",&a,&b);
dp[i][a]=b;
}
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=j)dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
}
}
}
vector<int>ans1,ans2;
ll m1=0xfffffffffffff,m2=0xfffffffffffff;
for(int i=1;i<=n;i++)
{
ll ma=0;
for(int j=1;j<=n;j++)
{
if(i==j||f[i]==f[j])continue;
ma=max(ma,dp[j][i]);
}
if(f[i])
{
if(m1>ma)m1=ma,ans1.clear();
if(m1==ma)ans1.pd(i);
}else
{
if(m2>ma)m2=ma,ans2.clear();
if(m2==ma)ans2.pd(i);
}
}
int e=0;
for(auto i:ans1)
{
if(!e)e=1;
else cout<<" ";
cout<<i;
}
cout<<endl;
e=0;
for(auto i:ans2)
{
if(!e)e=1;
else cout<<" ";
cout<<i;
}
}
L3
L3-1 千手观音 -分数 30
拓扑排序题 ,由于给的每个数字都是严格递增的,那么对于每个位数相同的数字,都可以连边,计算入度,例如,1,123,132,222,这3个数字,我们根据123,132,可以推断出2<3,根据123,222可以推断出1<2,那么可以推出1<2<3.,而1和其他数位数不同,不可以直接推出关系。连完边后拓扑排序,因为给出的拓扑排序不一定是可以推出唯一拓扑序的,所以根据题目要求还需要对于同时在queue里的字母,按字典序排序,我们把queue换成priority_queue即可。
代码:
//---------------------------↓乱定义区------------------------------------
int n,cnt;
unordered_map<string,int>id;
unordered_map<int,string>name;
vector<int>v[maxn],ve[maxn];
set<pair<int,int>>st;
int ru[maxn];
//---------------------------↓乱写乘区------------------------------------
void tp()
{
priority_queue<string,vector<string>,greater<string>>p;
for(int i=1;i<=cnt;i++)
{
if(ru[i]==0)p.push(name[i]);
}
int e=0;
while(!p.empty())
{
string now=p.top();p.pop();
if(!e)e=1;
else cout<<".";
cout<<now;
for(int i:ve[id[now]])
{
ru[i]--;
if(ru[i]==0)p.push(name[i]);
}
}
}
void solve()
{
cin>>n;
cnt=0;
for(int i=0;i<n;i++)
{
string s,e="";
cin>>s;
for(int j=0;j<s.length();j++)
{
if(s[j]!='.')
{
e+=s[j];
}
if(s[j]=='.'|| j==s.length()-1)
{
if(!id[e])id[e]=++cnt,name[cnt]=e;
v[i].pd(id[e]);
e="";
}
}
}
for(int i=0;i<n-1;i++)
{
if(v[i].size()!=v[i+1].size())continue;
for(int j=0;j<v[i].size();j++)
{
if(v[i][j]==v[i+1][j])continue;
if(st.find({v[i][j],v[i+1][j]})!=st.end())break;
st.insert({v[i][j],v[i+1][j]});
ve[v[i][j]].pd(v[i+1][j]);
ru[v[i+1][j]]++;
break;
}
}
tp();
}
L3-2 关于深度优先搜索和逆序对的题应该不会很难吧这件事 -分数 30
根据样例解释可以发现,(1)对于节点5,他的子节点1,2,无论如何排列,在dfs序中都必在5后,而4,对于1,2节点,不是在任何dfs中都有贡献的。
(2)对于节点3、5,由于3是5的双亲节点,那么根据(1)可以知道3必在5的前面,且在所有dfs序排列中都不会有贡献,对于节点5,1,就是在所有dfs序排列中都会产生贡献
(3)在一个1-n不重复序列的全排列中一个数字出现在另一个数字前的概率是1/2,也就是会有n!/ 2个逆序对,而在这个dfs排列中,对于2个非有祖先关系的2个点,其中一个点出现在另一个节点的概率可以发现也是是1/2,又由于2个节点值不同,所以必然会在一半的排列中会产生贡献。
我们不妨对与所有节点对,都假设都会在一半的dfs序排列中都会产生贡献,那么就不需要考虑(3)这个条件了,只需要看对于节点对中有祖先关系的,进行考虑。
对于祖先节点,和一个子节点,如果祖先节点的值比子节点大,那么我们少计算了一半贡献,如果小,那么我们多计算了一半贡献,只需要对应的加减即可。
那么我们只需要dfs遍历,然后通过树状数组(Fenwick Tree / Binary Indexed Tree)计算这个节点的祖先节点有多少比他大,有多少比他小即可。复杂度O(n*log(n))
代码:
//---------------------------↓乱定义区------------------------------------
int n,r;
vector<int>v[maxn];
ll c[maxn],fuda[maxn],fuxiao[maxn],jc[maxn],num[maxn];
ll ans;
//---------------------------↓乱写乘区------------------------------------
int lowbit(int x)
{
return x&(-x);
}
void add(int x,ll e)
{
for(;x<=n;x+=lowbit(x))c[x]+=e;
}
ll sum(int x)
{
ll ans=0;
for(;x>0;x-=lowbit(x))ans+=c[x];
return ans;
}
ll ksm(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void dfs(int rt,int fa)
{
fuda[rt]=sum(n)-sum(rt);
fuxiao[rt]=sum(rt-1);
num[rt]=1;
add(rt,1);
for(int &i:v[rt])
{
if(i==fa)continue;
dfs(i,rt);
num[rt]=num[rt]*num[i]%mod;
}
add(rt,-1);
num[rt]=num[rt]*jc[v[rt].size()-1]%mod;
}
void solve()
{
cin>>n>>r;
jc[0]=1;
for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
for(int i=1,a,b;i<n;i++)
{
cin>>a>>b;
v[a].pd(b);
v[b].pd(a);
}
v[r].pd(0);
dfs(r,0);
ans=0;
ll two=ksm(2,mod-2);
ans=1ll*n*(n-1)/2%mod*num[r]%mod*two%mod;
for(int i=1;i<=n;i++)
{
ans+=fuda[i]*num[r]%mod*two%mod;
ans-=fuxiao[i]*num[r]%mod*two%mod;
ans=(ans+mod)%mod;
}
cout<<ans<<endl;
}
L3-3 教科书般的亵渎 -分数 30
待补状态┭┮﹏┭┮,只想到暴力骗7分,不知道dp咋推
在暴力过程中发现第一张牌不可以理解为K轮每轮造成1点伤害,不然样例推不出来,也就是第一张牌实际上是随机选择一些牌造成总和为K的伤害,每个敌人的血都可以扣成负数。emmm。