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,yx,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,yk),而本身 ( 0 , x , x + y ) (0,x,x+y) (0,x,x+y)就能到达所有的这些情况,这就形成了博弈论中喜闻乐见的一种结构。 B ∈ n e x t ( A ) B \in next(A) Bnext(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;
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐