思维day2
P7150 [USACO20DEC] Stuck in a Rut S
题面
我们可以知道奶牛的轨迹是一条线。
首先我们注意到一个性质:每只奶牛只会被不同方向的奶牛所阻碍(x,y坐标各不相同)。
观察数据范围,我们发现只能从行进方向入手。
考虑奶牛什么时候会停下:当且仅当两头方向不同的奶牛所行进的轨迹产生交点
我们只需要维护交点即可,重点既是如何记录交点以及交点需要维护哪些信息。
产生交点的条件:
1)两头奶牛的方向不同
2)假设向东的奶牛为#c_{i}#,向北的奶牛为\(c_{j}\),则产生交点的条件为:
\(c_{i}.x<c_{j}.x\)且\(c_{i}.y>c_{j}.y\)
现在还要判断交点被阻挡的奶牛是哪只,只需记录两头牛的起始点与交点的距离
所以交点维护的信息已经推导完毕
(i)交点的坐标
(ii)由哪两条线相交(线的编号)
但我们会发现一个问题,我们储存的是一条射线,但实际上会有奶牛被阻碍导致射线被阻断。
我们可以设置一个删除数组,维护每一头奶牛有没有没阻碍,在判断交点时如果有其中至少一头牛被打上了删除标记,则这个交点是不存在的。
对于没有被阻碍的牛,它阻碍的牛的个数应该是:
它在此交点前挡住的牛+此交点的另一头牛阻碍的牛+1(另一头牛本身)
我们还有一个小问题:判断交点顺序
相交的距离越小的点就越先被判断,显然是左下角的点优先被判断(因为牛是往右上方走的)
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1007;
int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-48;ch=getchar();}x=(f==-1?-x:x); return x;
}
struct node{int x,y,val;
}c[N],nth[N],est[N];
struct point{int x,y,numx,numy;bool operator<(const point& o)const{if(x==o.x) return y<o.y;return x<o.x;}
}q[N*N>>2];
int n,cnt,cne,cnp,ans[N];
bool del[N];
void solve(){n=read();for(int i=1;i<=n;i++){char op;node p;op=getchar(),getchar();p=(node){read(),read(),i};if(op=='N') c[i]=p,nth[++cnt]=p;else c[i]=p,est[++cne]=p;}for(int i=1;i<=cnt;i++)for(int j=1;j<=cne;j++)if(nth[i].x>est[j].x && nth[i].y<est[j].y)q[++cnp]={nth[i].x,est[j].y,est[j].val,nth[i].val};sort(q+1,q+cnp+1);for(int i=1,dx=q[i].x-c[q[i].numx].x,dy=q[i].y-c[q[i].numy].y;i<=cnp;i++,dx=q[i].x-c[q[i].numx].x,dy=q[i].y-c[q[i].numy].y)if(del[q[i].numx] || del[q[i].numy]) continue;else if(dx<dy) {del[q[i].numy]=1,ans[q[i].numx]+=ans[q[i].numy]+1;}else if(dx>dy) {del[q[i].numx]=1,ans[q[i].numy]+=ans[q[i].numx]+1;}for(int i=1;i<=n;i++) cout<<ans[i]<<"\n";
}
int main(){ios::sync_with_stdio(false);cin.tie(0);int T=1;while(T--) solve();
}
P8266 [USACO22OPEN] Photoshoot B
题面
我们可以把题目抽象表示。
每次反转一个偶数长度的前缀事实上就是把翻转的前缀中的奇数位与偶数位翻转。
直接的说,如果我们让两个字母为一组,若翻转覆盖了这一组,则这两个字母的奇偶会交换。
显然如果两个字母是相同的,那么怎么交换都是没用的,目标是让更多的G在偶数位置上
实现:
当相邻两个字母不一样时,我们将G在左侧的看作1,反之看作0,那么我们会得到一个01串,目标是
让1翻转为0,那么题目就变成硬币翻转
这道题了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
char s[200005];
int ans,n;
vector <int> p;
int main(){ios::sync_with_stdio(false);cin.tie(0);cin>>n;cin>>(s+1);for(int i=1;i<=n;i+=2)if(s[i]!=s[i+1])if(s[i]=='G') p.push_back(1);else p.push_back(0);for(int i=1;i<p.size();i++) if(p[i]!=p[i-1]) ans++;if(p.size()==1) cout<<ans;else cout<<ans+p[p.size()-1];
}
P7299 [USACO21JAN] Dance Mooves S
题面
注意到因为交换会进行无数次,所以我们考虑到:有牛A从x->y,而牛B由z->x,那么再过一轮B也会走到y
也就是A与B的运功轨迹是一样的,于是有相同运动轨迹的牛会构成一个集体,而一个集体在k分钟内的运动轨迹会形成一个环。我们可以用并查集维护这个集体,set统计集体的途径点,每个独立集体内的牛能经过的点的个数就是set维护的个数
点击查看代码
#include<bits/stdc++.h>using namespace std;
int n,k;
int x,y;
const int N=1e5+7;
int a[N],fa[N];
vector <int> v[N];
set<int> ans[N];
void init(){for(int i=1;i<=n;i++)fa[i]=a[i]=i;
}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main(){ios::sync_with_stdio(false);cin.tie(0);cin>>n>>k;init();for(int i=1;i<=k;i++){int x,y;cin>>x>>y;swap(a[x],a[y]);v[a[x]].push_back(y);v[a[y]].push_back(x);}for(int i=1;i<=n;i++)v[i].push_back(i);for(int i=1;i<=n;i++){int u=find(i),v=find(a[i]);fa[u]=v;}for(int i=1;i<=n;i++)for(int j:v[a[i]])ans[find(a[i])].insert(j);for(int i=1;i<=n;i++) cout<<ans[find(i)].size()<<"\n";
}