做题小结
第一个

这道题和之前牛客寒假营的数三角形很像,严重怀疑是不是从这改编的
这题还好 我做的时候就是一个递归吧,从上到下 每一行的所有元素直接互相比较 然后进行连线处理 就是一开始脑子傻了 对于二维的前缀和 我只开了一个左上到右下的
殊不知这样右上到左下即使凑不够删除的值他会用这个前缀和的值 然后造成可以删除
所以要单独开两个前缀和数组即可 这里不需要用特殊的对角线处理 只需要枚举即可 然后其实你会发现这二者之间元素个数和层数是一个等差数列
然后这个是不可能有偶数的 所以偶数要之间continue 这博客园用不太来
`
for (int i = 1; i <= n; i++)
memset(temp, 0, sizeof temp);
int step = 0;
if (v[i].size()) {
for (auto j : v[i])temp[++step] = j;
for (int j = 1; j <= step; j++) {
for (int k = j + 1; k <= step; k++) {
int y = temp[j];
int yy = temp[k];
int dis = yy - y -1;
if((dis+2)%2==0)continue;//思考不全
if(dis==0)continue;
int cengshu=(dis+3)/2-1;
if (sum[i + cengshu][y + cengshu] - sum[i - 1][y - 1] >= cengshu+1 ) {
if (ssum[i + cengshu][yy -cengshu] - ssum[i - 1][yy + 1] >= cengshu+1) {
if (cengshu >= d)
{
check(i, y, yy, cengshu);
}
}
}
}
}
}
`
第二个

这题是个贪心的优先队列
我当时写的不够贪心 仅仅是大的先和次大用 这样累计下去
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int range = 2e5 + 10;
int n;
struct node {
int id;
int num;
} a[range];
int step;
pair<int, pair<int, int>>p[range];
void init() {
for (int i = 1; i <= step; i++) {
p[i].first = 0;
p[i].second = {0, 0};
}
}
bool cmp(node x, node y) {
return x.num > y.num;
}
void solve(int t) {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].num, a[i].id = i;
if(t==69)
{
cout<<n<<",";
for(int i=1;i<=n;i++)cout<<a[i].num<<",";
cout<<endl;
return ;
}
sort(a + 1, a + 1 + n, cmp);
int l = 1;
int num = 0;
int r = 2;
int step = 0;
while (a[l].num >= 1 && r <= n&&l<=n) {
if(a[r].num==0)break;
if (a[l].num - a[r].num > 0) {
a[l].num -= a[r].num;
num += a[r].num;
p[++step].first = a[r].num;
p[step].second.first = a[l].id;
p[step].second.second = a[r].id;
r++;
} else {
num += a[l].num;
p[++step].first = a[l].num;
p[step].second.first = a[l].id;
p[step].second.second = a[r].id;
a[r].num -= a[l].num;
if (a[r].num == 0) {
l = r + 1;
r += 2;
} else {
l = r;
r++;
}
}
}
cout << num << endl;
if (num != 0) {
for (int i = 1; i <= step; i++) {
for (int j = 1; j <= p[i].first; j++) {
cout << p[i].second.first << " " << p[i].second.second << endl;
}
} cout<<endl;
}
init();
return ;
}
signed main() {
int t ;
cin >> t;
for(int i=1;i<=t;i++)
solve(i);
}
6
2 5 3 0 0 2
这种就要出事 实际代码好像是6次 我弄了五次 应该运用优先队列的沉浮的优势
别的就没啥说了 由于这个题影响导致了我后面一道题目也是不需要用优先队列。。这是后话
`
while (q.size()) {
auto u = q.top();
q.pop();
if(q.empty())break;
auto v = q.top();
q.pop();
if(v.num>0&&u.num>0){
p[++step] = {v.id, u.id};
}
v.num--;
u.num--;
if (v.num>0) {
q.push({v.id, v.num});
}
if (u.num>0) {
q.push({u.id, u.num});
}
}
`
第三个

这个题的话 是求逆序对最小 然后
贪心的思路很好想 以致于我都不敢下手 怕贪错了 后面想了半天验证 感觉这个是没有后效性的 于是下手写 就对了
但是这个题我也写了题解 在洛谷上 这个不用线段树的话要用orderset
代码不放了 这个稍后把题解重新上传
第四个

这题一开始没看数据想复杂了 想成分层图的做法了
后面看到数据果断放弃 毕竟建边都建不了 于是发现了可以对0进行分析 因为0才是会是的1变0的东西 于是考虑就行bfs染色 于是即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int range = 2e5 + 10;
int n;
struct node {
int id;
int num;
} a[range];
int step;
pair<int, pair<int, int>>p[range];
void init() {
for (int i = 1; i <= step; i++) {
p[i].first = 0;
p[i].second = {0, 0};
}
}
bool cmp(node x, node y) {
return x.num > y.num;
}
void solve(int t) {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].num, a[i].id = i;
if(t==69)
{
cout<<n<<",";
for(int i=1;i<=n;i++)cout<<a[i].num<<",";
cout<<endl;
return ;
}
sort(a + 1, a + 1 + n, cmp);
int l = 1;
int num = 0;
int r = 2;
int step = 0;
while (a[l].num >= 1 && r <= n&&l<=n) {
if(a[r].num==0)break;
if (a[l].num - a[r].num > 0) {
a[l].num -= a[r].num;
num += a[r].num;
p[++step].first = a[r].num;
p[step].second.first = a[l].id;
p[step].second.second = a[r].id;
r++;
} else {
num += a[l].num;
p[++step].first = a[l].num;
p[step].second.first = a[l].id;
p[step].second.second = a[r].id;
a[r].num -= a[l].num;
if (a[r].num == 0) {
l = r + 1;
r += 2;
} else {
l = r;
r++;
}
}
}
cout << num << endl;
if (num != 0) {
for (int i = 1; i <= step; i++) {
for (int j = 1; j <= p[i].first; j++) {
cout << p[i].second.first << " " << p[i].second.second << endl;
}
} cout<<endl;
}
init();
return ;
}
ps 这题分层图我想了半天 看到数据范围都傻了 浪费好久时间
第五个

这题的思路就在于数据范围 看到1e9 直接想到暴力把所有的2的幂打出来 然后一个个对着匹配就行了
然后这题我当时犯傻了 对于谁匹配谁没好好验证 理所应当的认为反正都是匹配谁匹配的更多用谁但还这个思路是错的
n可以删除任意数位 难道2的幂可以吗 不行的呀 所以我们只有一个匹配方式 还是错了我才知道 回头看代码一测拍大腿
`
string temp = s[x];
string hs = to_string(n);
int len1 = temp.size();
int len2 = hs.size();
int step2 = 0;
for (int i = 0; i <= len2 - 1; i++) {
if (temp[step2] == hs[i])step2++;
}
`
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int range = 2e5 + 10;
int n;
string s[range];
int num;
void init() {
for (int i = 1; i <= 1e18; i *= 2)
s[++num] = to_string(i);
}
int check(int x, int n) {
string temp = s[x];
string hs = to_string(n);
int len1 = temp.size();
int len2 = hs.size();
//1504 1048576 step1 3 step2 2
//1504 4
//int step1 = 0;
int step2 = 0;
//拿 要变的做爹
// for (int i = 0; i <= len1 - 1; i++) {
// if (hs[step1] == temp[i])step1++;
// }
//拿自己做爹 要变得做儿子
//错误的 就不能拿要变的做爹 这绝对是错的做法
for (int i = 0; i <= len2 - 1; i++) {
if (temp[step2] == hs[i])step2++;
}
int ans2 = len2 - step2 + len1 - step2;
return ans2;
}
//我好弱智啊 自己给自己想复杂了 一开始就想到正解了
//然而不舍得去草稿纸画两下 浪费这么久 思考出来还要去证明 否则别动手写
void solve(int t) {
cin >> n;
int ans = 1e12;
for (int i = 1; i <= num; i++) {
ans = min(check(i, n), ans);
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio();
cin.tie(0);
cout.tie(0);
init();
int t;
cin >> t;
for(int i=1;i<=t;i++)
solve(i);
return 0;
}
第六个题

这题说实话我是真的对找初始串想了真的很久好几个小时 我不断模拟 还是没找到办法
后面一看题解 草 竟然是次数/删的顺序 我当时拿题没几分钟就想出来了 删的顺序以为自己能很快做出来的 靠
对于-1很明显就是我们出现次数不匹配和最终模拟出现答案不符
`
cin >> s;
int len = s.size();
deque<char>q;
unordered_map<char, int>ma;
for (int i = len - 1; i >= 0; i--) {
if (ma[s[i]]==0) {
q.push_front(s[i]);
// ma[s[i]]++;
}
ma[s[i]]++;
}
int step = 0;
while (!q.empty()) {
auto u = q.front();
q.pop_front();
del[++step] = u;
}
int num = 0;
for (int i = 1; i <= step; i++) {
if (ma[del[i]] % i) {
cout << -1 << endl;
return ;
} else
num += ma[del[i]] / i; ;
}
string ans = s.substr(0, num);
string temp = ans;
len=ans.size();
set<char>S;
for (int i = 1; i <= step; i++) {
S.insert(del[i]);
for (int j = 0; j <= len - 1; j++) {
if (S.count(ans[j]))
continue;
else {
temp += ans[j];
}
}
}
string ans2;
for(int i=1;i<=step;i++)
ans2+=del[i];
if(temp!=s){cout<<-1<<endl;return ;}
else cout<<ans<<" "<<ans2<<endl;
`
第七个题

这个题目我还真没想到怎么写 后面看了题解才明白,对于那种k=2我们可以搜索找到所有的值
说实话 我就是这个深搜写不出来 没思路写 这个时间复杂度是C(10,2)*2^10次方 不过我写成11次方了
不过这都没什么,写10次方的话时间复杂度是 𝑂(𝑇log𝑚),𝑚<5×10^4 然后注意到双层for循环第二层从i+1开始 不然都重复了
void dfs(int k,int x,int y,int val)
{
// 3 8
if(k>9)return ;
if(k>=1)
num2[++step2]=val;
dfs(k+1,x,y,val*10+x);
dfs(k+1,x,y,val*10+y);
}
for(int i=0;i<=9;i++)
{
for(int j=i+1;j<=9;j++)
{
dfs(0,i,j,0);
}
}
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int range=2e5+10;
int n;
int k;
int a[range];
int step1;
int step2;
int num1[range];
int num2[range];
set<int>s1;
set<int>s2;
void dfs(int k,int x,int y,int val)
{
// 3 8
if(k>9)return ;
if(k>=1)
num2[++step2]=val;
dfs(k+1,x,y,val*10+x);
dfs(k+1,x,y,val*10+y);
}
void init()
{
for(int i=1;i<=9;i++)
{
int k=0;
for(int j=1;j<=9;j++)
{
num1[++step1]=k+i;
k+=i*(pow(10,j));
}
}
// cout<<step1<<endl;
for(int i=0;i<=9;i++)
{
for(int j=i+1;j<=9;j++)
{
dfs(0,i,j,0);
}
}
//c(10,2) 25 dfs 深度是2的11次方
for(int i=1;i<=step1;i++)
{
s1.insert(num1[i]);
s2.insert(num1[i]);
// cout<<num1[i]<<" ";
}
// cout<<endl;
for(int i=1;i<=step2;i++)
{
s2.insert(num2[i]);
}
int x=1111111111;
s1.insert(x);
x=1e9;
s2.insert(x);
//草 这个没有的
}
void solve(int t)
{
//也许你不会懂 从你说爱我以后
//我的天空 星星都亮了
cin>>n>>k;
//相爱的把握
if(k==2)
{
auto it=s2.lower_bound(n);
cout<<*it<<endl;
return ;
}
else {
auto it=s1.lower_bound(n);
cout<<*it<<endl;
}
}
signed main()
{
ios::sync_with_stdio();
cin.tie(0);
cout.tie(0);
init();
int t;
cin>>t;
for(int i=1;i<=t;i++)
solve(i);
return 0;
}
第八题

这个染色的题目的话 我做错了
超时了反正
代码
//愚公移的是我写的石山
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int range = 4e5 + 10;
int n;
int k;
int a[range];
struct node {
int id;
int num;
friend bool operator<(node x, node y) {
return x.num < y.num;
}
};
priority_queue<node>temp;
vector<int>v[range];
int cnt[range];
int ccnt[range];
vector<int>e[range];
void init() {
for (int i = 0; i <= n+1000; i++) {
cnt[i] = 0; ccnt[i]=0; e[i].clear(); v[i].clear();
}
}
pair<int, vector<int>>check(int mid) {
priority_queue<node> q = temp;
vector<int>ans;
ans.resize(2 * n);
unordered_map<int,set<int>>mma;
while (q.size()) {
auto u = q.top();
q.pop();
int one = u.id;
int two = u.num;
cnt[one]++;
cnt[one] = min(cnt[one], k + 1);
int index = cnt[one];
while (v[index].size() >= mid||mma[index].count(one))
{
// (find(v[index].begin(),v[index].end(),one)!=v[index].end())
//二分也可 不过要排序更麻烦了
index++;
}
index = min(index, k + 1);
int w = v[index].size();
v[index].push_back(one);
mma[index].insert(one);
if (two > 1) {
q.push({one, two - 1});
}
}
for (int i = 1; i <= k; i++) {
if (v[i].size() >= mid)continue;
else {
init();
return {0, ans};
}
}
for (int i = 1; i <= k + 1; i++)
for (auto j : v[i]) e[j].push_back(i);
for (int i = 1; i <= n; i++) {
ccnt[a[i]]++;
if (e[a[i]].size() >= ccnt[a[i]] ) {
ans[i] = e[a[i]][ccnt[a[i]] - 1];
}
}
init();
return {1, ans};
}
void solve(int t) {
cin >> n;
cin >> k;
while(temp.size())temp.pop();
unordered_map<int, int>ma;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ma[a[i]]++;
}
if(n==1){
cout<<1<<endl;init();
return ;
}
for (auto i : ma) {
temp.push({i.first, i.second}); ;
}
int l = 1;
int r = n;
vector<int>hs;
while (l <= r) {
int mid = l + r >> 1;
pair<int, vector<int>>p = check(mid);
if (p.first) {
hs = p.second;
l = mid + 1;
} else r = mid - 1;
}
for(int i=1;i<=n;i++)
{
if(hs[i]>=k+1)hs[i]=0;
cout<<hs[i]<<" ";
}
cout << endl;
init();
}
signed main() {
ios::sync_with_stdio();
cin.tie(0);
cout.tie(0);
int t ;
cin>>t;
for(int i=1;i<=t;i++)
solve(i);
return 0;
}
我二分做的
脑残了 没想到k次以外的都不用管了 然后全部统计好k次以内的 求个最大分组就好了 我是傻逼
这题我说的优先队列乱用 其实不需要优先队列用了还多个log草 但是超时不是这个log引起的 而是那个while循环引起的
然后染色的话 只需要循环染色即可 这样也不会重复 因为组数是一定的 然后我后面还写成同一个数之内循环染色 真是猪 很明显这样答案就要偏小了
void solve() {
cin >> n;
cin >> k;
map<int, int>maa;
map<int, vector<int>>ma;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (maa[a[i]] < k) {
ma[a[i]].push_back(i);
maa[a[i]]++;
}
}
int num = 0;
for (auto i : ma) {
num += i.second.size();
}
int ans = num - num % k;
int col = 0;
for ( auto i : ma) {
for (auto j : i.second) {
b[j] = ++col;
col %= k;
//这两步很好 我想了下还没想到这么%着用
ans--;
if (ans == 0)goto shuchu;
}
}
// for( auto i:ma)
// {
// int col=1;
// for(auto j:i.second)
// {
// b[j]=col;
// col++;
// ans--;//写错了猪 不是说一组内的col循环!!犯傻了
// if(ans==0)goto shuchu;
//// if(col)
// }
// }
shuchu:
for (int i = 1; i <= n; i++)cout << b[i] << " ";
return ;
}
题

这是一道很好玩的题目
其实回头看 牛客多校也考了这个题
首先题目读不懂这很正常 因为图都不给 我看翻译真看傻了 然后对于这个题 我觉得以后对图形找规律要善于转化 从大推小 比如这个题
要坚定自己的思路 不要想着ififif
代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define re return ;
using namespace std;
const int range=2e5+10;
int n;
int m;
int k;
int a[range];
void solve()
{
//哎 我想到了要转化的 可是自己为什么就那么排斥这种做法呢
//小镇做题家的思维告诉我 应该转化找规律吧
//自己为什么不坚持下去呢 非要觉得自己摸样例就觉得自己能特判成功
//怎么可能呀!!! 所以对于脑海出现的可能正解 要去坚持他
cin>>n>>m>>k;
int z=n*m/2;
int kk=z-k;
if(n%2==0&&m%2==0)
{
if(k%2||(z-k)%2){
cout<<"NO"<<endl;
}
else cout<<"YES"<<endl;
return ;
}
if(n%2==0&&m%2)
{
if(kk<n/2){cout<<"NO"<<endl;re}
int newn=n;
int newm=m-1;
int newk=newn*newm/2;
if(k>newk){cout<<"NO"<<endl;re}
if(k%2==0&&(kk-n/2)%2==0&&(k+kk-n/2)==newk){
cout<<"YES"<<endl;
re
}
cout<<"NO"<<endl;
re
}
else if(n%2&&m%2==0)
{
if(k<m/2){cout<<"NO"<<endl;re}
int newn=n-1;
int newm=m;
int newk=newn*newm/2;
if(kk>newk){cout<<"NO"<<endl;re}
if(kk%2==0&&(k-m/2)%2==0&&(k+kk-m/2)==newk){
cout<<"YES"<<endl;
re
}
cout<<"NO"<<endl;
re
}
}
题

这道题我一开始用的spfa去做 结果t 没办法 我看那个数据就知道被卡了 30万的长度 太夸张了 直线的进行最短路
被卡是很正常的 然后换用dj就行了 反正没负权 直接用上dj就行 spfa被卡了 这个出题人内测应该是有人写了然后顺手卡了
for(int i=1;i<=n;i++)
{
if(i==1)v[i].push_back({i+1,1});
else if(i==n)v[i].push_back({i-1,1});
else {
v[i].push_back({i+1,1});
v[i].push_back({i-1,1});
}
}
题目

这个题目 我当时写的二分 结果tle 为什么我敢写二分因为我算了下最大的约束也就240 以为最多答案来到240
是自己低估了人家造数据的本事 只要很多的前后相等的数这个答案就会无限扩大
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int range=4e5+10;
int n;
int a[range];
int b[range];
//和风诉说我们的过往
bool check(int mid)
{
//1e6 最多的一个数也就240个约数
//也就是说不断取gcd 最多200多次就行了 所以Olog300*n*x*t*gcd
for(int i=1;i<=n;i++)b[i]=a[i];
for(int j=1;j<=mid;j++)
{
int x=b[1];
for(int i=1;i<=n;i++)
{
// a[i]=__gcd(a[i],a[(i%n)+1]);
if(i==n)b[i]=__gcd(b[i],x);
else b[i]=__gcd(b[i],b[i+1]);
}
}
for(int i=1;i<=n-1;i++)
{
if(b[i]==b[i+1])continue;
else return 0;
}
}
void init()
{
for(int i=1;i<=n+100;i++)b[i]=0;
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int l=0;
int r=300;
int ans=400;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid))
{
// cout<<mid<<endl;
ans=min(ans,mid);
r=mid-1;
}
else l=mid+1;
}
init();
cout<<ans<<endl;
}
最重要是要看出来进行x次操作 其实就是求ai-ai+x%n的gcd 我这个没看出来 可惜了 真的很可惜 没发现这个点
然后我后面又想着类似于归并那样做
这种思路虽然用不到这个题 但是是很重要的 这个分治思想!
但是很明显不行 递归回去又会进行操作 有后效性
然后就直接上线段树就行 这个线段树阉割了很多操作的 都不用写什么pushdown这些
struct node {
int l, r, gcdd;
} tr[range * 4];
void pushup(int u) {
tr[u].gcdd = __gcd(tr[2 * u].gcdd ,tr[u * 2 + 1].gcdd);
}
void build(int u, int l, int r) {
tr[u] = {l, r, w[l]};
if (l == r)return ;
int mid = l + r >> 1;
build(2 * u, l, mid);
build(2 * u + 1, mid + 1, r);
pushup(u);
}
//
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u].gcdd;
}
int mid = (tr[u].l + tr[u].r) >> 1;
int sum = 0;
if (l <= mid)sum = __gcd( query(2 * u, l, r), sum);
if (r > mid)sum = __gcd( query(2 * u + 1, l, r), sum);
return sum;
}
bool check(int mid) {
set<int>s;
for(int i=1;i<=n;i++)
{ int x=query(1,i,i+mid);s.insert(x);}
if(s.size()==1)return 1;
else return 0;
}
void init()
{
for(int i=1;i<=4*n+100;i++)tr[i]={0,0,0};
for(int i=1;i<=2*n;i++)w[i]=0;
}
void solve() {
cin >> n ;
for (int i = 1; i <= n; i++)cin >> w[i];
for (int i = n + 1; i <= 2 * n; i++)w[i] = w[i - n];
build(1, 1, 2 * n);
int l = 0;
int r = n;
int ans = 0;
while (l <= r) {
int mid = l + r >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else l = mid + 1;
}init();
cout << ans << endl;
}

浙公网安备 33010602011771号