2023ICPC第一场网络赛 VP题解
2023ICPC第一场网络赛VP题解~
2023ICPC第一场网络赛VP题解
2023赛季打完了,先把之前跟队友疯狂VP但没补的场次标一下,然后滚去准备期末考试,考完试再来好好研究
A(签到)
签到题,队友签的
题目大意
给定根据网络赛的总排名规则,和两次网络赛的榜单,让你模拟一下
需要注意的点
1.使用map的时候注意如果映射的像是string这类的,如果键值对不存在,返回的值会是默认值
2.补题的时候因为变量名的标号写错而WA了一发
#include<bits/stdc++.h>
using namespace std;
map<int,string> ran1,ran2;
map<string,bool> vis1,vis2,vis;
vector<string> ans;
void work()
{
int n,m;
cin>>n>>m;
int tot1=0,tot2=0;
for(int i=1;i<=n;i++)
{
string str;
cin>>str;
if(!vis1[str]) {ran1[++tot1]=str;vis1[str]=true;}
}
for(int i=1;i<=m;i++)
{
string str;
cin>>str;
if(!vis2[str]) {ran2[++tot2]=str;vis2[str]=true;}
}
for(int i=1;i<=max(tot1,tot2);i++)
{
if(i<=tot1)
{
if(!vis[ran1[i]]) vis[ran1[i]]=true,ans.push_back(ran1[i]);
}
if(i<=tot2)
{
if(!vis[ran2[i]]) vis[ran2[i]]=true,ans.push_back(ran2[i]);
}
}
for(auto item:ans)
cout<<item<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
//cin>>t;
while(t--)
work();
return 0;
}
D(图论)
题目大意
给定一张非完全无向图(不一定连通),要求添加一些边(至少一条)使得图具有传递性,问最少的边数
思路
分两种情况即可
1.所有连通块都是完全图,那么答案就是合并最小的两个连通块
2.不是所有连通块都是完全图,那么答案就是把他们补全
需要注意的点
1.VP时直接define int long long MLE了
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
const int inf=0x3fffffff;
int n_cnt,bm[maxn],bn[maxn],n,m;
struct Edge
{
int next;int to;
}edge[maxn];
int cnt,head[maxn];
bool vis[maxn];
void addedge(int from,int to)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
head[from]=cnt;
}
int summ=0,sumn=0;
void dfs(int u,int fat)
{
vis[u]=true;sumn++;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;summ++;
if(v==fat) continue;
if(!vis[v]) dfs(v,u);
}
}
void work()
{
bool flag=false;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
addedge(x,y);
addedge(y,x);
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
summ=0,sumn=0;
n_cnt++;
dfs(i,0);
summ/=2;
if(summ!=sumn*(sumn-1)/2) flag=true;
bm[n_cnt]=summ;bn[n_cnt]=sumn;
}
}
if(flag)
{
long long ans=0;
for(int i=1;i<=n_cnt;i++)
ans+=((long long)bn[i]*(long long)(bn[i]-1)/2)-bm[i];
cout<<ans<<'\n';
}
else
{
long long ans=0;
long long d1=inf,d2=inf;
for(int i=1;i<=n_cnt;i++)
if(bn[i]<d1) d2=d1,d1=bn[i];
else if(bn[i]<d2) d2=bn[i];
ans=d1*d2;
cout<<ans<<'\n';
}
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
//cin>>t;
while(t--)
work();
return 0;
}
F(博弈论+计数,trie)
题目大意:
给定一个序列 a n a_n an,从中选出一个三元组进行游戏
游戏的规则是:每次从中选两个数将它们换成和不变,绝对值之差更小的两个数,无法操作者败
问有多少种选法满足先手必胜
思路:
先分析博弈论
1.我们只需关注三个点之间的间隔,因此 ( a , a + x , a + y ) (a,a+x,a+y) (a,a+x,a+y)等价于 ( 0 , x , y ) (0,x,y) (0,x,y),且 ( 0 , x , y ) (0,x,y) (0,x,y)等价于 ( 0 , y − x , y ) (0,y-x,y) (0,y−x,y),这些三元组的可操作集合是相同的
2.然后需要注意到 ( 0 , x , x + y ) (0,x,x+y) (0,x,x+y),变为 ( x , x , y ) (x,x,y) (x,x,y)后,只能选取 x , y x,y x,y操作,并且只能变成 ( x + k , x , y − k ) (x+k,x,y-k) (x+k,x,y−k),而本身 ( 0 , x , x + y ) (0,x,x+y) (0,x,x+y)就能到达所有的这些情况,这就形成了博弈论中喜闻乐见的一种结构。 B ∈ n e x t ( A ) B \in next(A) B∈next(A)且所有 n e x t ( B ) ∈ n e x t ( A ) next(B) \in next(A) next(B)∈next(A),不论 B B B是先手必胜还是先手必败,都可以得到 A A A先手必胜,说明 x ≠ 0 x \ne 0 x=0时, ( 0 , x , x + y ) (0,x,x+y) (0,x,x+y)为先手必胜态
3.开始推导 ( 0 , 0 , y ) (0,0,y) (0,0,y)的情况,由 2 2 2可知,只可能走到 ( 0 , y / 2 , y / 2 ) (0,y/2,y/2) (0,y/2,y/2),等价于 ( 0 , 0 , y / 2 ) (0,0,y/2) (0,0,y/2)。如果某一步 y y y不为偶数,则只能转化为先手必胜态。所以这时问题取决于 y y y的二进制表示下的最小的 1 1 1的位置的奇偶性,为偶则先手必败(从0开始计数)
至此,问题转化为计数问题
即从一些数中找出满足以下条件中的一个的三元组
1.有三个互不相同的值
2.有两个互不相同的值,且这两个值的差的二进制表示下的最小的 1 1 1的位置为奇
正面计算1.比较困难,考虑容斥,2.使用 t r i e trie trie树进行计算
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
typedef long long ll;
int n;
int t[maxn<<6][2],siz[maxn<<6];
map<ll,int> mp;
ll C3(int x)
{
return (ll)x*(x-1)*(x-2)/6;
}
void work()
{
cin>>n;
for(int i=1;i<=n;i++)
{
ll x;cin>>x;
mp[x]++;
}
int pos=0;
ll ans=C3(n);
for(auto item:mp)
{
int u=0;
ll x=item.first;
ans-=C3(item.second);
for(int i=0;i<=60;i++)
{
if(!t[u][x>>i&1]) t[u][x>>i&1]=++pos;
u=t[u][x>>i&1];siz[u]+=item.second;
}
}
for(auto item:mp)
{
int u=0;
ll x=item.first;
ll C2=(((ll)item.second)-1ll)*(item.second)/2;
for(int i=0;i<=60;i++)
{
if(i%2==0) ans-=(ll)C2*siz[t[u][(x>>i&1)^1]];
u=t[u][x>>i&1];
}
}
for(int i=0;i<=pos;i++)
t[i][0]=t[i][1]=siz[i]=0;
mp.clear();
cout<<ans<<'\n';
}
int main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
cin>>t;
while(t--)
work();
return 0;
}
G(队友过的,可补)
题目大意:
思路:
H(可补)
题目大意:
思路:
I(队友过的,可补)
题目大意:
思路:
J (签到)
题目大意:
给定两个圆 C 1 , C 2 C_1,C_2 C1,C2,保证两个圆内没有点的横纵坐标相同,在 C 2 C_2 C2上选一个点使得 C 1 C_1 C1区域内的点的期望曼哈顿距离最小
思路:
通过圆的对称性发现,一个点到圆内曼哈顿距离的期望,等价于圆心到这个点的曼哈顿距离
需要注意的点
输入浮点型变量比输入整型变量慢很多,VP时因为这个TLE了很多发
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;cin>>t;
double sq2=sqrt(2);
while(t--){
int x1,y1,x2,y2,x3,y3,x4,y4;
cin>>x1>>y1>>x2>>y2>>x3>>y3>>x4>>y4;
double xx1=(x1+x2),yy1=(y1+y2);
double xx2=(x3+x4),yy2=(y3+y4);
double xxxx = (double)(x3-x4)*(x3-x4);
double yyyy = (double)(y3-y4)*(y3-y4);
double r2=sqrt(xxxx+yyyy);
cout<<setprecision(9)<<(fabs(xx1-xx2)+fabs(yy1-yy2)-r2 * sq2)/2<<"\n";
}
return 0;
}
K(计算几何,积分)
题目大意:
给定一个凸包,和 n n n个圆,对于每个圆,在凸包内(包括边界)找到一个点,使得这个点到圆内所有点的期望平方距离最小
思路:
通过极坐标积分(余弦定理)求出一个点到圆内所有点的期望平方距离等于$\frac 1 2 r2+d2 $
然后就是判断圆心是否在凸包内,在的话 d = 0 d=0 d=0
不在的话转一遍所有边, d d d取最小线段距离
需要注意的点
无穷大的值不要取太小(WA了很多发)
#include<bits/stdc++.h>
#define double long double
using namespace std;
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define sz(x) (int) (x).size()
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;
const double eps=1e-6;
template<class T>
struct Point{
typedef Point P;
T x,y;
explicit Point (T x=0,T y=0) : x(x),y(y) {}
bool operator ==(P p) const{return abs(x-abs(p.x))<eps&&abs(y-abs(p.y))<eps;}
bool operator < (P p) const{return tie(x,y)<tie(p.x,p.y);}
P operator +(P p) const {return P(x+p.x,y+p.y);}
P operator -(P p) const {return P(x-p.x,y-p.y);}
P operator *(T d) const {return P(x*d,y*d);}
P operator /(T d) const {return P(x/d,y/d);}
T dot(P p) const {return x*p.x+y*p.y;}
T cross(P p) const {return x*p.y-y*p.x;}
T cross(P a,P b) const {return (a-*this).cross(b-*this);}
T dist2() const {return x*x+y*y;}
double dist() const {return sqrt((double)dist2());}
P unit() const{return *this/dist();}
P perp() const{return P(-y,x);}
P normal() const {return perp().unit();}
P rotate(double a) const{
return P(x*cos(a)-y*sin(a),x*sin(a)+y*cos(a));}
};
typedef Point<double> P;
double segDist(P& s,P& e,P& p)
{
if(s==e) return (p-s).dist();
auto d=(e-s).dist2(),t=min(d,max((double)0.0,(p-s).dot(e-s)));
return ((p-s)*d-(e-s)*t).dist()/d;
}
bool onSegment(const P&s,const P&e,const P&p)
{
P ds=p-s,de=p-e;
return abs(ds.cross(de))<eps&&ds.dot(de)<=0;
}
template<class P>
int sideOf(const P& s,const P& e,const P& p)
{
auto a=(e-s).cross(p-s);
return (a>0)-(a<0);
}
template<class P>
int sideOf(const P& s,const P& e,const P& p,double eps)
{
auto a=(e-s).cross(p-s);
double l=(e-s).dist()*eps;
return (a>l)-(a<-l);
}
typedef Point<double> P;
int insideHull2(const vector<P>& H,int L ,int R,const P&p)
{
int len=R-L;
if(len==2){
int sa=sideOf(H[0],H[L],p,eps);
int sb=sideOf(H[L],H[L+1],p,eps);
int sc=sideOf(H[L+1],H[0],p,eps);
if(sa<0||sb<0||sc<0) return 0;
if(sb==0||(sa==0&&L==1)||(sc==0&&R==sz(H))) return 1;
return 2;
}
int mid=L+len/2;
if(sideOf(H[0],H[mid],p,eps)>=0)
return insideHull2(H,mid,R,p);
return insideHull2(H,L,mid+1,p);
}
int insideHull(const vector<P> &hull ,const P& p)
{
if(sz(hull)<3) return onSegment(hull[0],hull.back(),p);
else return insideHull2(hull,1,sz(hull),p);
}
int n,q;
void work()
{
typedef Point<double> P;
vector<P> poly;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
{
P nod;
cin>>nod.x>>nod.y;
poly.push_back(nod);
}
while(q--)
{
P p1,p2;
cin>>p1.x>>p1.y>>p2.x>>p2.y;
P O=(p1+p2)/2;double r2=(p2-p1).dist2()/4;
if(insideHull(poly,O)!=0)
printf("%.12Lf\n",r2/2);
else
{
double minn=1e12;
for(int i=0;i<n;i++)
{
if(i==0) minn=min(minn,segDist(poly[0],poly[n-1],O));
minn=min(minn,segDist(poly[i],poly[i-1],O));
}
printf("%.12Lf\n",r2/2+minn*minn);
}
}
}
int main()
{
//cin.tie(0);
//cin.sync_with_stdio(0);
int t=1;
//cin>>t;
while(t--)
work();
return 0;
}
L(签到)
题目大意:
签到题,懒得翻译
思路:
直接模拟
需要注意的点
第一道签到题,VP的时候太急了没读完,忘记max上2了
#include<bits/stdc++.h>
#define int long long
using namespace std;
void work()
{
int n,tim,maxx=-1;
cin>>n>>tim;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
maxx=max(maxx,x);
}
cout<<max(2ll,(maxx+tim-1)/tim)<<'\n';
}
signed main()
{
cin.tie(0);
cin.sync_with_stdio(0);
int t=1;
//cin>>t;
while(t--)
work();
return 0;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)