I. Left Shifting
题目链接👈
题目描述🥰

题目思路😀
简单模拟即可
AC代码🧠
void solve()
{
string s;
cin>>s;
int n=s.size();
if(s[0]==s[n-1]){
cout<<0<<endl;
return;
}
for(int i=0;i<n-1;i++)if(s[i]==s[i+1]){
cout<<i+1<<endl;
return;
}
if(s[0]!=s[n-1]){
cout<<-1<<endl;
}
}
A. Printer
题目链接👈
题目描述🥰

题目思路😀
这个题目是简单的题目,但是坑也是真的多,vp的时候贡献了好几发罚时😰。
思路就是一个简单的二分,对时间进行二分即可。
坑1:数据的溢出
我们在check函数的过程中可能会有数据的溢出,所以我们需要过程中已经满足返回条件就可以直接返回即可,不要等到最后才返回,这样就能有效的避免过程中的数据溢出(贡献了7发罚时才找出的错误😅)
坑2:二分的答案范围
这个题目也是特殊到极致了,二分的最大值如果取1e18并不能满足答案的范围🤯,要开2e18才能过💩(第一次见😭)
AC代码🧠
void solve()
{
int n,k;
cin>>n>>k;
vector<edge>a(n);
for(int i=0;i<n;i++){
cin>>a[i].t>>a[i].l>>a[i].w;
}
int l=1,r=2e18;
auto check=[&](int mid)->bool{
int res=0;
for(int i=0;i<n;i++)
{
int sum=a[i].t*a[i].l+a[i].w;
int cnt=mid/sum;
res+=cnt*a[i].l+min((mid%sum)/a[i].t,a[i].l);
if(res>=k)return true;
}
return res>=k;
};
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
K. Matrix
题目链接👈
题目描述🥰

题目思路😀
一个比较简单的构造题目,构造思路首先把不同的四个数字填到左上角,然后向左向下进行拓展
举一个n=5的例子来看,我们就可以和下面一样填

Question:如何满足一定出现了元素1~2*n呢?
除了左上角的4个元素,横向有n-2种元素,纵向有n-2种元素,再加上左上角的四种元素,就等于2*n种元素
AC代码🧠
这个代码的判断no的代码纯属多余😂
const int N=55;
int g[N][N];
void solve()
{
int n;
cin>>n;
g[1][1]=1;
g[1][2]=2;
g[2][1]=3;
g[2][2]=4;
int cnt=5;
for(int j=3;j<=n;j++)
{
g[1][j]=cnt;
g[2][j]=cnt;
cnt++;
}
for(int i=3;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
g[i][j]=cnt;
}
cnt++;
}
map<int,int>hash;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
hash[g[i][j]]++;
}
}
if(hash.size()<2*n){
cout<<"No"<<endl;
return;
}
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<<g[i][j]<<" ";
}
cout<<endl;
}
}
C. Colorful Segments 2
题目链接👈
题目描述🥰

题目思路😀
我们需要计算和每一个线段重叠的线段个数ai,答案就是(k-a1)*(k-a2)*(k-a3)...*(k-an)
那么为什么呢?
因为我的每一个线段的涂色只需要和重叠线段的涂色不一样即可,所以就有(k-ai)种可能,根据乘法原理答案就是(k-a1)*(k-a2)*(k-a3)...*(k-an)
但是注意代码实现不要偏了,不要使用双指针来求和每一个线段重叠的线段个数ai, 理由见下图

我们双指针的计算在遍历到ai的时候,会找到第一个和ai重叠的区间aj,所以会误认为把两者之间的线段都视为重叠,而其实不是的,如上图,虽然1和3重叠,但是1和3直接的2并不和3重叠,所以双指针的实现思路会有问题
而正确思路就是我们需要用一个小根堆来记录之前的区间的右端点,保证当前的区间的左端点小于堆顶元素后,这样就可以保证堆里面的区间都是和当前区间重合的。
AC代码🧠
struct edge{
int l;
int r;
bool operator<(const edge& b)const
{
if(l==b.l)return r<b.r;
return l<b.l;
}
};
void solve()
{
int n,k;
cin>>n>>k;
vector<edge>a(n);
for(int i=0;i<n;i++)
{
cin>>a[i].l>>a[i].r;
}
sort(a.begin(),a.end());
int ans=1LL;
priority_queue<int,vector<int>,greater<int>>q;
for(int i=0;i<n;i++)
{
// while(j<i&&a[i].l>a[j].r)j++;
while(q.size()&&q.top()<a[i].l)q.pop();
ans=ans*(k-q.size())%MOD;
q.push(a[i].r);
}
cout<<ans<<endl;
}
F. Divide the Sequence
题目链接👈
题目描述🥰

题目思路😀
vp的时候是队友写出来的,但是赛后补题的时候我自己看了很久😇
这个题目让我觉得最妙的思路就是在于把乘法化成加法,题目要求的是i*si,你可以把这个视为si被加了i遍,那么题目就会清晰很多了
我们需要求的是把数组分成k份的时候的最大值,我们可以从1开始看起,如果数组被分成1份,那么最大值就是sum本身。如果被分成2份,就相当于某一个后缀会再加一次,为了最大化,那么我们肯定加最大的那个后缀,后面分成3份也是同样的道理。
因为题目中的n是5e5,所以我们需要先预处理后缀和并排序,分成k份,答案就是前k-1大的后缀相加+sum。
AC代码🧠
void solve()
{
int n;
cin >> n;
vector<int> a(n+1);
int sum=0;
for (int i = 1; i <=n; ++i) cin >> a[i],sum+=a[i];
vector<int>pre(n+2,0);
//预处理后缀和
for(int i=n;i>=1;i--)pre[i]=pre[i+1]+a[i];
//把2~n的后缀和从大到小进行排列
sort(pre.begin()+2,pre.begin()+n+1,greater<int>());
vector<int>ans(n+1);
ans[1]=sum;
for(int i=2;i<=n;i++)ans[i]=ans[i-1]+pre[i];
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
cout<<endl;
}
J. Colorful Spanning Tree
题目链接👈
题目描述🥰

题目思路😀
我们要保证权重最小,那么我们的每一个颜色可以留一个和其他颜色来连通,其他都和连接代价最小的颜色进行连接即可,最后把剩下的每一个颜色跑一个最小生成树算法即可。
AC代码🧠
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int ans = 0;
vector<vector<int>> g(n, vector<int>(n));
for (int i = 0; i < n; i++) {
int res = 1e9;
for (int j = 0; j < n; j++) {
cin >> g[i][j];
res = min(g[i][j], res);
}
ans += res * (a[i] - 1);
}
// prim
vector<int> dis(n, 1e9);
vector<int> vis(n, 0);
dis[0] = 0;
for (int i = 0; i < n; i++) {
int u = -1;
for (int j = 0; j < n; j++) {
if (!vis[j] && (u == -1 || dis[j] < dis[u])) u = j;
}
vis[u] = 1;
ans += dis[u];
for (int k = 0; k < n; k++) {
dis[k] = min(dis[k], g[u][k]);
}
}
cout << ans << endl;
}
posted on
浙公网安备 33010602011771号