【福建集训】贪心(2/3)
A.涂色方案
首先想到根据已有信息推出一个某种颜色的“最大矩形”,处在矩形中的其他颜色会将这个矩形判定为“被覆盖”,这种颜色就是不能出现的。这样很容易可以写出暴力思路,可以顺带用链表优化
60pts:
#include <bits/stdc++.h> using namespace std; #define re register #define fo1(l, r) for (re int i = l; i <= r; ++i) #define fo2(l, r) for (re int j = l; j <= r; ++j) #define fo3(l, r) for (re int k = l; k <= r; ++k) #define fo4(l, r) for (re int tt = l; tt <= r; ++tt) #define fo(l) for (re int i = h[l], go; i; i = x[i].last) #define inf 0x3f3f3f3f #define INF 0x7fffffffffffffff #define LL long long #define itn int #define DB double inline int read() { int x = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar(); } return x * f; } const int MA = 1e6 + 10; int a[MA], n, m, k; inline int yget(int hh, int ll) { return (hh - 1) * m + ll; } inline int min2(int xx, int yy) { return xx < yy ? xx : yy; } inline int max2(int xx, int yy) { return xx > yy ? xx : yy; } int minnh[MA], maxnh[MA]; int minnl[MA], maxnl[MA]; bitset<MA> pd; vector<pair<int, int> > v; int cnt; struct node { node *nxt; int data; } * p, *h, *r, *q; int main() { freopen("paint.in", "r", stdin); freopen("paint.out", "w", stdout); n = read(); m = read(); k = read(); memset(minnh, 127, sizeof(minnh)); memset(maxnh, 128, sizeof(maxnh)); memset(minnl, 127, sizeof(minnl)); memset(maxnl, 128, sizeof(maxnl)); v.push_back(make_pair(0, 0)); h = new node; h->data = 0; r = new node; h->nxt = r; r->data = -1; int ty = 0; fo1(1, n) { fo2(1, m) { re int ls = yget(i, j); a[ls] = read(); if (a[ls] == 0) continue; v.push_back(make_pair(i, j)); minnh[a[ls]] = min2(minnh[a[ls]], i); minnl[a[ls]] = min2(minnl[a[ls]], j); maxnh[a[ls]] = max2(maxnh[a[ls]], i); maxnl[a[ls]] = max2(maxnl[a[ls]], j); if (pd[a[ls]] == 0) { ++ty; pd[a[ls]] = 1; p = new node; r->nxt = p; r->data = a[ls]; p->data = -1; r = p; } } } if (ty == 1) { printf("%d", k - 1); return 0; } fo4(1, v.size() - 1) { re int i = v[tt].first; re int j = v[tt].second; int ls = yget(i, j); p = h->nxt; q = h; while (1) { re int k = p->data; if (k == -1) break; if (k != a[ls] && i >= minnh[k] && i <= maxnh[k] && j >= minnl[k] && j <= maxnl[k]) { q->nxt = p->nxt; ++cnt; break; } p = p->nxt; q = q->nxt; } } cnt = k - cnt; printf("%d", cnt); fclose(stdin); fclose(stdout); return 0; }
正解是差分思路,其实也很直接的
#include<bits/stdc++.h> using namespace std; #define re register #define fo1(l,r) for(re int i=l;i<=r;++i) #define fo2(l,r) for(re int j=l;j<=r;++j) #define fo3(l,r) for(re int k=l;k<=r;++k) #define fo4(l,r) for(re int tt=l;tt<=r;++tt) #define fo(l) for(re int i=h[l],go;i;i=x[i].last) #define inf 0x3f3f3f3f #define INF 0x7fffffffffffffff #define LL long long #define itn int #define DB double inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)) { x=(x<<3)+(x<<1)+ch-48; ch=getchar(); } return x*f; } const int N=5e5+10; int a[N];bitset<N> pd;bitset<N> pdd;int sp;//特殊判断 itn b[N]; int n,m,k; inline int yget(int hh,int ll) { return hh*(m+2)+ll; } int minh[N],minl[N],maxh[N],maxl[N],ANS; inline int min2(int xx,int yy) { return xx<yy?xx:yy; } inline int max2(int xx,int yy) { return xx>yy?xx:yy; } int main() { freopen("paint.in","r",stdin); freopen("paint.out","w",stdout); n=read();m=read();k=read(); memset(minh,127,sizeof(minh)); memset(minl,127,sizeof(minl)); memset(maxh,128,sizeof(maxh)); memset(maxl,128,sizeof(maxl)); fo1(1,n) { fo2(1,m) { int ls=read(),now=yget(i,j); if(pd[ls]==0) { pd[ls]=1; ++sp; } a[now]=ls; minh[ls]=min2(minh[ls],i); minl[ls]=min2(minl[ls],j); maxh[ls]=max2(maxh[ls],i); maxl[ls]=max2(maxl[ls],j); } } if(k>1 && sp==1) { printf("%d",k-1); return 0; } fo1(1,k) { if(pd[i]!=0) { // cout<<"颜色"<<i<<" "<<minh[i]<<" "<<minl[i]<<"&"<<maxh[i]<<" "<<maxl[i]<<endl; ++b[yget(maxh[i]+1,maxl[i]+1)]; --b[yget(maxh[i]+1,minl[i])]; --b[yget(minh[i],maxl[i]+1)]; ++b[yget(minh[i],minl[i])]; } } ANS=k; fo1(0,n) { fo2(0,m) { int now=yget(i,j); if(i>=1) b[now]+=b[yget(i-1,j)]; if(j>=1) b[now]+=b[yget(i,j-1)]; if(i>=1&&j>=1) b[now]-=b[yget(i-1,j-1)]; // cout<<"当前"<<"("<<i<<","<<j<<") "<<b[now]<<endl; if(b[now]>1) { if(pdd[a[now]]==0) { pdd[a[now]]=1; --ANS; } } } } printf("%d",ANS); fclose(stdin); fclose(stdout); return 0; }
B.钱仓问题
首先容易想到一个贪心思路是每次把现在这一堆的所有金币顺次移到最后一个空位上
比如:
空1 空2 空3 1 2
首先把1移到空1,再把2分别移到空2,空3
但是这样有个问题就是一定存在一个断点,使得从这里开始,不能越过进行运输
比如:空1与空2中间有断点
空1 | 空2 空3 1 2
则对于1,先移到空2
对于2,先移到空3,再到1所在的腾出来的位置
我们发现这样的断点并不是任意选择的
比如:
空1 | 空2 空3 4 2
对于4会无“空位置”可以转运了,一旦出现这种情况说明我们的断点是不合法的
合法的断点的选择是有暴力思路的,就是O(n)枚举,再O(n)判断合不合法。然而事实上我们可以直接一轮O(n)求出来断点,因为对于一个合法断点,其每一位的“前缀和”一定不会比0小,如果比0小我们就把断点往前推直到比0大
(64pts)O(n*n)没写正解
#include<bits/stdc++.h> using namespace std; #define re register #define fo1(l,r) for(re int i=l;i<=r;++i) #define fo2(l,r) for(re int j=l;j<=r;++j) #define fo3(l,r) for(re int k=l;k<=r;++k) #define fo4(l,r) for(re int tt=l;tt<=r;++tt) #define fo(l) for(re int i=h[l],go;i;i=x[i].last) #define inf 0x3f3f3f3f #define INF 0x7fffffffffffffff #define LL long long #define itn int #define DB double inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)) { x=(x<<3)+(x<<1)+ch-48; ch=getchar(); } return x*f; } const int N=1e5+10; int c[N],sum,st,now,bh,a[N]; LL ANS; queue<int> q; int main() { freopen("barn.in","r",stdin); freopen("barn.out","w",stdout); itn n=read(); fo1(1,n) c[i]=read(); for(re int tt=n;tt>=1;--tt) { now=tt;bh=1; while(1) { a[bh]=c[now]; --now;++bh; if(now==0) now=n; if(bh>n) break; } now=1; bool pd=0;ANS=0; while(now<=n) { if(a[now]!=0) { while(!q.empty() && a[now]!=0) { LL ls1=q.front()-now; q.pop(); ANS+=ls1*ls1; --a[now]; } } if(a[now]==0) q.push(now); else { if(a[now]!=1) { pd=1; break; } } ++now; } if(pd==0) { printf("%lld",ANS); return 0; } } fclose(stdin); fclose(stdout); return 0; }=

浙公网安备 33010602011771号