1. 程式人生 > 實用技巧 >2019-2020 ACM-ICPC Brazil Subregional Programming Contest

2019-2020 ACM-ICPC Brazil Subregional Programming Contest

2019-2020 ACM-ICPC Brazil Subregional Programming Contest

比賽情況

我們一共過了7道題,ABDGHLM,如果失誤再少點,JK可做

本場貢獻:et3_tsy :6min讀完B,完成簽到。四小時過了D。

1427314831a:1小時37過了中檔題L。

Ryker0923 :16min完成H。56min過了M。三小時過了A題。四小時過了G。

一共七道題

罰時:et3_tsy AD加起來差不多十幾發, Ryker0923 GM加起來差不多十幾發

比賽總結


et3_tsy

這場同時出現在現場的隊伍比較多,而且勢均力敵。不知道隊友怎麼感受的,但反正我是感覺有點壓力,或者說,受到其他隊伍影響比較大(會比較緊張,打亂了自己的節奏),所以以後心理抗壓這方面要注意,以及不要過分關注榜單情況。

首先是隊友M二分那邊翻車了,不知道犯了一個什麼神奇的問題,我也沒有問,去想A了。我A過於自信,沒有讀樣例三,就感覺是並查集,和隊友確定了演算法和題意後直接碼。瘋狂WA,後來才發現樣例三有點問題,隊友Ryker0923改了挺久的過了。我得到1427314831a給我的D題題意後,一下子就想到了正解,等A改完後碼。可能是我注意力不集中,樹剖寫的過程中忘記取了一個地方的max了,自造的小資料一點沒問題,一直在WA。

這場比賽後,我讀懂了J後,20min左右寫了J,A掉,其實我想如果我們前面沒有犯各種奇奇怪怪的錯誤,我們是有可能能做出9道題的(隊友的K似乎也找到規律了)

我感覺我這場的A,犯了一個很大的問題,就是確認樣例,樣例給的多,再耗時間,再麻煩,一定

要全部手動求解,以防有些題目本身沒有講清楚的地方在樣例中體現

以及D題,對於資料結構題而言,當WA了兩次(即改過一次後),還是WA的話,優先選擇debug,而不是去構造資料,去輸出一下看看每步應該存的東西對不對。如果某個陣列到某處的值與理想不同,應該看看是不是什麼函式寫了忘記呼叫(如常忘的init()),以及一些取max,min的,還有全域性區域性變數的問題,以及物件要不要加引用去繫結(比如一些圖論中的邊可能要被標記,這個時候迭代器不只是一個clone,要能修改原物件)


1427314831a


Ryker0923


部分題解

BH.簽到

A. 並查集

這題題意有點沒講清楚,反反覆覆在猜題意(實際上如果讀通樣例3就不會犯這樣的錯誤)

#include<bits/stdc++.h>
using namespace std;
int fa[1010];
int m,n,k;
long long r[1010][3];
bool u[1010][4],used[4];
int find(int k)
{
    if(fa[k]==k)return k;
    return fa[k]=find(fa[k]);
}
bool ins(int x,int y)
{
	long long rd=(r[x][2]+r[y][2])*(r[x][2]+r[y][2]);
	long long dis=(r[x][0]-r[y][0])*(r[x][0]-r[y][0])+(r[x][1]-r[y][1])*(r[x][1]-r[y][1]);
	if(rd>=dis)
	return 1;
	else
	return 0;
}
int main()
{
	cin>>m>>n>>k;
	for(int i=1;i<=k;i++)
	{
		cin>>r[i][0]>>r[i][1]>>r[i][2];
		fa[i]=i;
	}
	for(int i=1;i<=k;i++)
	for(int j=i+1;j<=k;j++)
	if(ins(i,j))
	{
		int x=find(i);
		int y=find(j);
		fa[x]=y;
	}
	for(int i=1;i<=k;i++)
	{
		if(r[i][0]-r[i][2]<=0)
		u[fa[i]][0]=1;
		if(r[i][0]+r[i][2]>=m)
		u[fa[i]][1]=1;
		if(r[i][1]-r[i][2]<=0)
		u[fa[i]][2]=1;
		if(r[i][1]+r[i][2]>=n)
		u[fa[i]][3]=1;
		if(u[fa[i]][0]==1&&u[fa[i]][2]==1)
		used[0]=1;
		if(u[fa[i]][0]==1&&u[fa[i]][3]==1)
		used[1]=1;
		if(u[fa[i]][1]==1&&u[fa[i]][2]==1)
		used[2]=1;
		if(u[fa[i]][1]==1&&u[fa[i]][3]==1)
		used[3]=1;
	}
//	for(int i=1;i<=k;i++)
//	cout<<u[i][0]<<" "<<u[i][1]<<" "<<u[i][2]<<" "<<u[i][3]<<" "<<endl;
	for(int i=1;i<=k;i++)
	{
		int sum=0;
//		for(int j=0;j<=3;j++)
//		sum+=u[i][j];
//		cout<<sum<<endl;
		if((u[i][0]==1&&u[i][1]==1)||(u[i][2]==1&&u[i][3]==1))
		{
			cout<<"N";
			return 0;
		}
//		if(sum>=2)
//		for(int j=0;j<=3;j++)
//		used[j]=used[j]?1:u[i][j];	
	}
//	for(int i=0;i<=3;i++)
//	cout<<used[i]<<" ";
//	cout<<endl;
//	if(used[0]==1&&used[1]==1)
//	cout<<"N";
//	else if(used[0]==1&&used[2]==1)
//	cout<<"N";
//	else if(used[1]==1&&used[3]==1)
//	cout<<"N";
//	else if(used[2]==1&&used[3]==1)
//	cout<<"N";
	if(used[0]==1||used[3]==1)
	cout<<"N";
	else
	cout<<"S";
	return 0;
}

D.長鏈剖分+堆

首先我們進行長鏈剖分,使得每步貪心最優,利用堆儲存每個輕孩子,在堆中按照長度優先進行儲存,暴力跑出k條即可。

#include<bits/stdc++.h>
#include<ctime>
#define maxn 1000050
using namespace std;
struct e
{
    int depth;
    int index;
    e(int a,int b)
    {
        depth=a;
        index=b;
    }
    bool operator< (const e&sec)const
    {
        return depth<sec.depth;
    }
};

priority_queue<e>q;

int been[maxn];
int ans=0;

struct treenode
{
    int fa,hson,sz,depth,top,ds;
    vector<int>nxt;    
}t[maxn];  
int n,p,cur;
int dfs1(int pos)
{
    int ret=0;
    int ldis=0;
    t[pos].hson=-1;
    t[pos].sz=1;
    for(int k:t[pos].nxt)
    {
        if(!t[k].depth)
        {
            
            t[k].depth=t[pos].depth+1;
            t[k].fa=pos;
            int dis=dfs1(k);
            ret=max(dis,ret);
            if(t[pos].hson==-1||ldis<dis)
            {
                t[pos].hson=k;
                ldis=dis;
            }
        }
    }
    ret++;
    t[pos].ds=ret;
    return ret;
}

int main()
{
    cin>>n>>p;
    for(int i=2;i<=n;i++)
    {
        cin>>cur;
        t[cur].nxt.push_back(i);
        t[i].nxt.push_back(cur);
    }

    t[1].depth=1;
    dfs1(1);

    

    q.push(e(t[1].depth,1));
    while(q.size()&&p--)
    {
        int x=q.top().index;q.pop();
        int fa=x;
        ans++;
        been[x]=1;
        //cout<<x<<endl;
        while(t[x].hson!=-1)
        {
            //cout<<t[x].hson<<endl;
            for(int k:t[x].nxt)
            {
                if(k!=t[x].hson&&!been[k]&&fa!=k)q.push(e(t[k].ds,k));
            }
            fa=x;
            been[t[x].hson]=1;
            ans++;

            x=t[x].hson;
            
            //cout<<x<<endl;
        }
    }
    cout<<ans<<endl;
	return 0;
}

J.模擬

模擬題意即可,有可能開局天胡,注意細節。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
using namespace std;
#define INF 0x3f3f3f3f
#define max(a,b) (a>b ? a:b)
#define min(a,b) (a<b ? a:b)
#define swap(a,b) (a^=b^=a^=b)
#define maxn 105
#define minn -105
#define ll long long int
#define ull unsigned long long int
#define uint unsigned int
inline int read()
{
    int ans=0;
    char last=' ',ch=getchar();
    while(ch<'0'|ch>'9')last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans;
    return ans;
}
string s="A23456789DQJK",tt;
char a;
int n,k;
int ttt;
struct e
{
    int save[15];
    int out()
    {
        int curnum=10,curval=-1;
        for(int i=0;i<15;i++)
        {
            if(save[i]<curnum&&save[i])
            {
                curval=i;
                curnum=save[i];
            }
        }
        save[curval]--;
        return curval;
    }
    bool check()
    {
        bool mt=0;
        int cnt=0;
        for(int i=0;i<15;i++)
        {
            cnt+=save[i];
            if(save[i]==4)mt=1,ttt=i;
        }
        return cnt==4&&mt;
    }

}t[15];

int main()
{
    bool go=1;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>tt;
        for(int j=0;j<4;j++)
        {
            t[i].save[s.find(tt[j])]++;
        }
        if(k!=i&&t[i].check())go=0;
    }
    int cur=k;
    bool ok=0;
    while(go)
    {
        int tmp=-1;
        if(cur==k)
        {
            if(ok==0)
            {
                ok=1;
                tmp=t[cur].out();
            }
            else
            {
                k++;
                if(k==n+1)k=1;
                ok=0;
                if(t[cur].check())go=0;
            }
        }
        else
        {
            tmp=t[cur].out();
            if(t[cur].check())go=0;
        }
        //cout<<tmp<<endl;

        cur++;
        if(cur==n+1)cur=1;
        if(tmp!=-1)t[cur].save[tmp]++;
        if(k!=cur&&t[cur].check())go=0;
    }


    int curans=0,miin=1000;
    for(int i=1;i<=n;i++)
    {
        if(k!=i&&t[i].check()&&miin>ttt)
        {
            miin=ttt;
            curans=i;
        }
    }
    cout<<curans<<endl;


    return 0;
}
/*
    2 1
    33J3
    JJJ3

    2 2
    A2A2
    22AA

    4 2
    774Q
    JJQ7
    44Q7
    4QJJ

    3 1
    JQAA
    JJJA
    QQQA
*/

I:Floyd

類似於洛谷的災後重建,離線之後從溫度最大和溫度最小兩個方向

按順序加點跑兩次floyd。

#include<bits/stdc++.h>
using namespace std;
int n,m,cnt,cnt1,cnt2,q;
int ma1[510][510],ma2[510][510];
int tp[1010],l[1010];
struct query{
	int x,y,k,xl,ans;
}q1[100010],q2[100010];
void add(int x,int y,int z)
{
	ma1[x][y]=z;
	ma1[y][x]=z;
	ma2[x][y]=z;
	ma2[y][x]=z;
}
void floyd1(int k)
{
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	ma1[i][j]=ma1[j][i]=min(ma1[i][j],ma1[i][k]+ma1[k][j]);
}
void floyd2(int k)
{
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	ma2[i][j]=ma2[j][i]=min(ma2[i][j],ma2[i][k]+ma2[k][j]);
}
bool cmp1(query a,query b)
{
	return a.k<b.k;
}
bool cmp2(query a,query b)
{
	return a.xl<b.xl;
}
void lsh()
{
	for(int i=1;i<=n;i++)
	l[i]=tp[i];
	sort(l+1,l+n+1);
	cnt=unique(l+1,l+n+1)-l-1;
	for(int i=1;i<=n;i++)
	tp[i]=lower_bound(l+1,l+cnt+1,tp[i])-l;
}
int main()
{
	cin>>n>>m;
	int x,y,z,t;
	for(int i=1;i<=n;i++)
	scanf("%d",&tp[i]);
	lsh();
//	for(int i=1;i<=n;i++)
//	cout<<tp[i]<<" ";
//	cout<<"\n";
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	if(i!=j)
	ma1[i][j]=ma2[i][j]=0x3f3f3f3f;
	for(int i=1;i<=m;i++)
	scanf("%d%d%d",&x,&y,&z),add(x,y,z);
	cin>>q;
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d%d",&x,&y,&z,&t);
		if(t==0)
		q1[++cnt1].x=x,q1[cnt1].y=y,q1[cnt1].k=z,q1[cnt1].xl=i;
		else
		q2[++cnt2].x=x,q2[cnt2].y=y,q2[cnt2].k=z,q2[cnt2].xl=i;
	}
	sort(q1+1,q1+cnt1+1,cmp1);
	sort(q2+1,q2+cnt2+1,cmp1);
	int ltp=0,htp=cnt+1;
	for(int i=1;i<=cnt1;i++)
	{
		while(q1[i].k>ltp)
		{
			ltp++;
			for(int j=1;j<=n;j++)
			if(tp[j]==ltp)
			{
//				cout<<tp[j]<<" ";
//				cout<<"1:"<<" "<<i<<" "<<j<<endl;
				floyd1(j);
			}
		}
		q1[i].ans=ma1[q1[i].x][q1[i].y];
	}
	for(int i=1;i<=cnt2;i++)
	{
		while(cnt-q2[i].k+1<htp)
		{
			htp--;
			for(int j=1;j<=n;j++)
			if(tp[j]==htp)
			{
//				cout<<tp[j]<<" ";
//				cout<<"2:"<<" "<<i<<" "<<j<<endl;
				floyd2(j);
			}
			
		}
		q2[i].ans=ma2[q2[i].x][q2[i].y];
	}
	sort(q1+1,q1+cnt1+1,cmp2);
	sort(q2+1,q2+cnt2+1,cmp2);
	int t1=1,t2=1;
	for(int i=1;i<=q;i++)
	{
		if(q1[t1].xl==i)
		{
			printf("%d\n",q1[t1].ans==0x3f3f3f3f?-1:q1[t1].ans);
			t1++;
		}
		else
		{
			printf("%d\n",q2[t2].ans==0x3f3f3f3f?-1:q2[t2].ans);
			t2++;
		}
	}
	return 0;
}

G.二分圖帶權匹配 KM(或者費用流)

利用對數將乘法轉為加法,之後就是KM板子題

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
const ll Maxn=505;
const ll inf=1e18;
ll n,m,map[Maxn][Maxn],matched[Maxn];
ll slack[Maxn],pre[Maxn],ex[Maxn],ey[Maxn];//ex,ey頂標
bool visx[Maxn],visy[Maxn];
void match(ll u)
{	
    ll x,y=0,yy=0,delta;
    memset(pre,0,sizeof(pre));
    for(ll i=1;i<=n;i++)slack[i]=inf;
    matched[y]=u;
    while(1)
    {	
        x=matched[y];delta=inf;visy[y]=1;
        for(ll i=1;i<=n;i++)
        {	
            if(visy[i])continue;
            if(slack[i]>ex[x]+ey[i]-map[x][i])
            {	
                slack[i]=ex[x]+ey[i]-map[x][i];
                pre[i]=y;
            }
            if(slack[i]<delta){delta=slack[i];yy=i;}
        }
        for(ll i=0;i<=n;i++)
        {	
            if(visy[i])ex[matched[i]]-=delta,ey[i]+=delta;
            else slack[i]-=delta;
        }
        y=yy;
        if(matched[y]==-1)break;
    }
    while(y){matched[y]=matched[pre[y]];y=pre[y];}
}
ll KM()
{	
    memset(matched,-1,sizeof(matched));
    memset(ex,0,sizeof(ex));
    memset(ey,0,sizeof(ey));
    for(ll i=1;i<=n;i++)
    {	
        memset(visy,0,sizeof(visy));
        match(i);
    }
    ll res=0;
    for(ll i=1;i<=n;i++)
        if(matched[i]!=-1)res+=map[matched[i]][i];
    return res;
}
int main()
{	
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
        	cin>>map[i][j];
        	map[i][j]=log(map[i][j])*10000;
		}   
    KM();
    for(int i=1;i<=n;i++)
    {
    	if(i!=1)
    	printf(" ");
		printf("%lld",matched[i]);
	}
//    printf("\n");
    return 0;
}

L.

#include<bits/stdc++.h>
using namespace std;
long long ksm(long long a,int b)
{
	long long ans=1;
	while(b)
	{
		if(b&1)ans=ans*a;
		a*=a;
		b>>=1;
	}
	return ans;
}
int main()
{
	long long n;
	cin>>n;
	int sum=0;
	while(n)
	{
		sum+=n%2;
		n/=2;
	}
    cout<<ksm(2,sum);
	return 0; 
}

M.

#include<bits/stdc++.h>
using namespace std;
int n,t,c;
long long a[100010];
bool judge(long long x)
{
	int ans=0;
	long long sum=0;
	for(int i=1;i<=n;i++)
	{
//		cout<<i<<endl;
		sum+=a[i];
		if(sum/t+(sum%t!=0)>x)
		{
			i--;
			ans++;
			sum=0;
		}
//		else
//		sum+=a[i];
	}
	if(sum!=0)
	ans++;
	if(ans<=c)
	return 1;
	else
	return 0;
}
int main()
{
	cin>>n>>c>>t;
	long long maxn=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
//		if(a[i]%t==0)
//		a[i]/=t;
//		else
//		a[i]=a[i]/t+1;
		maxn=max(maxn,a[i]/t+(a[i]%t!=0));
	}
//	for(int i=1;i<=n;i++)
//	cout<<a[i]<<" ";
//	cout<<endl;
	long long l=maxn,r=10000000000000000ll,mid;
	while(l<r-1)
	{
		mid=(l+r)/2;
		if(judge(mid))
		r=mid;
		else 
		l=mid;
//		cout<<l<<" "<<r<<endl;
	}
	if(judge(l))
	cout<<l;
	else
	cout<<r;
	return 0; 
}