1. 程式人生 > 實用技巧 >Codeforces Round #545 Div1 題解

Codeforces Round #545 Div1 題解

Codeforces Round #545 Div1 題解

A-Skyscrapers

題意

​ 給定一個 \(n∗m\) 的網格,每個格子裡都有一個數,對於任意一行和任意一列,要求把這 \(n+m−1\) 個數重新用正整數編號,並且對於這一行,數與數之間的大小關係不變,對於這一列同理。求出任意一行和任意一列編號使用的最大編號的最小值。

題解

​ 首先對於每一行每一列單獨離散化,記錄每個位置在離散化之後的值,合併之後取較大的,剩下部分順次編號即可。

Code

#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m,r[N],l[N],a[N][N],b[N][N],c[N][N];
int s[N],top;

int main()
{
        scanf( "%d%d",&n,&m );
        for ( int i=1; i<=n; i++ )
         for (  int  j = 1; j<=m; j++ )
                scanf( "%d",&a[i][j] );
        
        for ( int i=1; i<=n; i++ )
        {
                top=0;
                for ( int j=1; j<=m; j++ )
                        s[++top]=a[i][j];
                sort( s+1,s+1+top ); top=unique(s+1,s+1+top)-s-1;
                for ( int j=1; j<=m; j++ )
                        b[i][j]=lower_bound( s+1,s+1+top,a[i][j] )-s;
                r[i]=top;
        }
        for ( int i=1; i<=m; i++ )
        {
                top=0;
                for ( int j=1; j<=n; j++ )
                        s[++top]=a[j][i];
                sort( s+1,s+1+top ); top=unique(s+1,s+1+top)-s-1;
                for ( int j=1; j<=n; j++ )
                        c[j][i]=lower_bound( s+1,s+1+top,a[j][i] )-s;
                l[i]=top;
        }

        for ( int i=1; i<=n; i++,printf("\n") )
         for ( int j=1; j<=m; j++ )
         {
                 int ans1=max( r[i],max( l[j],b[i][j]+l[j]-c[i][j] ) );
                 int ans2=max( r[i],max( l[j],c[i][j]+r[i]-b[i][j] ) );
                 printf( "%d ",max( ans1,ans2 ) );
         }
}

B-Camp Schedule

題意

​ 給定兩個 \(01\)\(S\)\(T\) ,把 \(S\) 打亂,使得 \(T\) 在其中出現次數最多。

題解

​ 對於 \(T\)\(KMP\) ,然後先建一次 \(T\) ,每次跳到 \(next\) 接著往後放即可

Code

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
char ch[N];
int n,m,nxt[N];

int main()
{
        scanf( "%s",ch+1 );
        for ( int i=1,l=strlen(ch+1); i<=l; i++ )
                if ( ch[i]=='0' ) n++;
                else m++;
        
        scanf( "%s",ch+1 ); int len=strlen(ch+1); nxt[1]=0;
        for ( int i=2; i<=len; i++ )
        {
                int tmp=nxt[i-1];
                while ( tmp && ch[tmp+1]!=ch[i] ) tmp=nxt[tmp];
                if ( !tmp )
                {
                        if ( ch[1]==ch[i] ) nxt[i]=1;
                        else nxt[i]=0;
                }
                else nxt[i]=tmp+1;
        }
        
        bool flag=1;
        for ( int i=1; i<=len; i++ )
                if ( ch[i]=='0' )
                {
                        if ( !n )  { flag=0; break; }
                        n--; printf( "0" );
                }
                else 
                {
                        if ( !m ) { flag=0; break; }
                        m--; printf( "1" );
                }
        while ( (n || m ) && flag )
        {
                for ( int i=nxt[len]+1; i<=len; i++ )
                        if ( ch[i]=='0' )
                        {
                                if ( !n )  { flag=0; break; }
                                n--; printf( "0" );
                        }
                        else 
                        {
                                if ( !m ) { flag=0; break; }
                                m--; printf( "1" );
                        }
        }
        while ( n-- ) printf( "0" );
        while ( m-- ) printf( "1" );
}

C-Museums Tour

題意

​ 給定一張 \(n\) 點的有向圖,每個點有博物館並給定每週開放狀態,一週的長度為 \(d\) 天,在某一週的第一天從 \(1\) 出發,每條邊邊權為 \(1\) ,途中不能逗留,問最多可以訪問幾個博物館。

題解

​ 首先根據時間拆點, \((i,j)\) 就是當前在城市 \(i\) ,星期 \(j\) ,對於原圖 \(u\to v\),連邊 \((u,i)\to (v,i \mod d+1)\)

​ 對這個圖求 \(SCC\) ,統計每個裡面有多少不同的博物館。然後找一條從 \(1\) 開始的最長鏈即可。

Code

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int head[N],head2[N];
struct edge
{
	int nxt,ver,val;
}e1[N<<1],e2[N*2*50];
int tot,tot2,cnt,n,m,d,gcd,g[N],f[N][55],pre[N][55],deg[N],val[N],c[N];
int dfn[N],low[N],num,top,sta[N];
char v[N][55];
bool vis[N],ins[N];
vector<int> scc[N];

void add( int x,int y )
{
	tot++; e1[tot]=(edge){ head[x],y,0 }; head[x]=tot;
}

void add2( int x,int y,int z )
{
	tot2++; e2[tot2]=(edge){ head2[x],y,z }; head2[x]=tot2; deg[y]++;
}

void solve( int x,int p )
{
	val[x]=p; vis[x]=1;
	for ( int i=head[x]; i; i=e1[i].nxt )
	{
		int y=e1[i].ver;
		if ( c[y]!=cnt ) continue;
		if ( !vis[y] ) solve(y,p+1);
		else gcd=__gcd( gcd,abs(p+1-val[y]) );
	}
}

void tarjan( int x )
{
	dfn[x]=low[x]=++num; sta[++top]=x; ins[x]=1;
	for ( int i=head[x]; i; i=e1[i].nxt )
		if ( !dfn[e1[i].ver] ) tarjan( e1[i].ver ),low[x]=min( low[x],low[e1[i].ver] );
		else if ( ins[e1[i].ver] ) low[x]=min( low[x],dfn[e1[i].ver] );
	if ( dfn[x]==low[x] )
	{
		cnt++; int y;
		do
		{
			y=sta[top--]; ins[y]=0; c[y]=cnt; scc[cnt].push_back(y);
		}while ( x!=y );
		gcd=d; solve( x,0 ); g[cnt]=gcd;
		for ( int i=0; i<gcd; i++ )
		 for ( int j=0; j<scc[cnt].size(); j++ )
		  for ( int k=i; k<d; k+=gcd )
		  	if ( v[scc[cnt][j]][k]=='1' ) 
		  	{
		  		v[scc[cnt][j]][i]='1'; break;
			}
		for ( int i=0; i<gcd; i++ )
		 for ( int j=0; j<scc[cnt].size(); j++ )
		 	if ( v[scc[cnt][j]][(val[scc[cnt][j]]+i)%gcd]=='1' ) pre[cnt][i]++;
	}
}

void dp()
{
	int ans=0; memset( f,-0x3f,sizeof(f) );
	f[c[1]][0]=pre[c[1]][0]; queue<int> q;
	for ( int i=1; i<=cnt; i++ )
		if ( !deg[i] ) q.push(i);
	while ( !q.empty() )
	{
		int x=q.front(); q.pop();
		for ( int i=head2[x]; i; i=e2[i].nxt )
		{
			int y=e2[i].ver; deg[y]--;
			if ( deg[y]==0 ) q.push(y);
		}
		for ( int i=head2[x]; i; i=e2[i].nxt )
		 for ( int j=0; j<d; j++ )
		 {
		 	int y=e2[i].ver,z=e2[i].val;
		 	f[y][(z+j)%d]=max( f[y][(z+j)%d],f[x][j]+pre[y][(z+j)%g[y]] );
		 }
		for ( int i=0; i<d; i++ )
			ans=max( ans,f[x][i] );
	}
	printf( "%d",ans );
}

int main()
{
	scanf( "%d%d%d",&n,&m,&d );
	for ( int i=1,u,v; i<=m; i++ )
		scanf( "%d%d",&u,&v ),add( u,v );
	for ( int i=1; i<=n; i++ )
		scanf( "%s",v[i] );
	
	for ( int i=1; i<=n; i++ )
		if ( !dfn[i] ) tarjan( i );
	for ( int j=1; j<=n; j++ )
	 for ( int i=head[j]; i; i=e1[i].nxt )
	 {
	 	int y=e1[i].ver;
	 	if ( c[j]==c[y] ) continue;
	 	int d2=__gcd(g[c[j]],g[c[y]] );
	 	int d1=(val[j]-val[y]+1+d)%d;
	 	for ( int k=d1%d2; k<d; k+=d2 )
	 		add2( c[j],c[y],k );
	 }
	 
	dp();
}


D-Cooperative Game (互動題)

題意

​ 有一張圖是由一個長度為 \(t\) 的鏈和一個大小為 \(c\) 的環中間連上一條邊組成的。假如這條邊連線的是鏈的右端點,和環上的 \(T\) 點。令鏈的左端點是 \(S\)
​ 現在在 \(S\) 處有 \(10\) 個棋子,編號 \(0−9\),每次你可以讓任意數量的棋子向出邊方向走一步,互動庫會返回若干個集合,每一個集合內的棋子都在同一個位置上,並且這個位置上的所有棋子都在這個集合中。
​ 現在你既不知道 \(t\) 也不知道 \(c\) 。你需要使用不超過 \(3(t+c)\) 次操作使得所有棋子都移動到T位置上並且返回互動庫done

題解

(第一次做互動題啊……)

​ 首先讓兩個棋子移動,一個每次操作都向前走,另外一個每兩次操作才向前走。當兩個棋子相遇時停止。
​ 把環按照 \(T\) 點開始沿出邊方向標號。
​ 設當第二個棋子位於 \(T\) ,即 \(0\) 位置時,假設第一個棋子在 \(p\) 位置。
​ 因為第一個棋子每次比第二個棋子多走一步,距離差是 \(c−p\)
​ 所以接下來第二個棋子要移動的步數就是 \(c−p\)。所以相遇時兩個棋子在 \(c−p\) 位置。
​ 因為第二個棋子到達 \(T\) 時走了 \(t\) 步,此時第一個棋子走了 \(2t\) 步,即他在環上走了 \(t\) 步,所以有 \(t\mod c=p\),那麼讓 \(10\) 個棋子同時向前走,當所有棋子位於同一個點時他們就同時到達了 \(T\)
​ 即這裡還需要走 \(t\) 步,而這 \(t\) 步等價於在環上走了 \(p\) 步,那麼 \(1,2\) 兩個棋子就從 \(c−p\) 走到了 \(0\) 位置。

​ 注意輸出要刷快取,可以用 cout 或者是 fflush(stdout).

Code

#include <bits/stdc++.h>
using namespace std;
char ch[20];

int read()
{
        int x; scanf( "%d",&x );
        for ( int i=1; i<=x; i++ )
                scanf( "%s",ch );
        return x;
}

int main()
{
	while ( 1 )
	{
		cout<<"next 0"<<endl; read();
		cout<<"next 0 1"<<endl;
		int tot=read();
		if ( tot==2 ) break;
	}
	while ( 1 )
	{
		cout<<"next 0 1 2 3 4 5 6 7 8 9"<<endl;
		if ( read()==1 ) break; 
	}
        cout<<"done";
}

E-Train Car Selection

題意

​ 你有一列有 \(n\) 個車廂的火車,從車頭開始 \(1−n\) 編號。
​ 現在有 \(3\) 種操作,第一種是在車頭位置加入 \(k\) 節車廂,第二種位置是在車尾位置加入 \(k\) 節車廂,第三種是修改每節車廂的價值。
​ 價值定義為:一開始所有車廂的價值都是 \(0\) (包括中途加入的車廂),然後每次修改會給定 \(s,b\),如果當前車廂是從車頭開始數的第 \(i\) 節,那麼它的價值就會加上 \((i−1)∗s+b\)
​ 在每次操作結束之後回答價值最小的車廂的編號以及其價值。如果有多個輸出編號最小的那個。

題解

​ 發現每次一起加入進來的東西只需要維護左端點就好了。
​ 首先如果在前端插入一段,那麼後面全部都沒用了,可以直接丟掉。
​ 否則在後面插入一段,維護一下當前全域性加的一次函式是什麼,那麼每個左端點按照車廂編號+權值可以寫成一個個的點,顯然只有一個上凸殼才有用,那麼維護這個上凸殼就行了。

Code

#include <bits/stdc++.h>
#define ll long long
#define PI pair<ll,ll>
using namespace std;
const int N=3e5+10;
PI p[N];
int n,m,r=1;
ll k=0,b=0;

double get_k( PI a,PI b )
{
        return 1.0*(a.second-b.second)/(a.first-b.first);
}

ll calc( PI x )
{
        return k*x.first+x.second+b;
}

int main()
{
        scanf( "%d%d",&n,&m );
        p[1]=make_pair( 0,0 ); r=1;
        while ( m-- )
        {
                int opt; scanf( "%d",&opt );
                if ( opt==1 ) 
                {
                        r=1; p[1]=make_pair( 0,0 ); k=b=0;
                        int tmp; scanf( "%d",&tmp ); n+=tmp;
                }
                else if ( opt==2 )
                {
                        PI now=make_pair( n,-(n*k+b) );
                        while ( r>1 && get_k(now,p[r])<=get_k( p[r],p[r-1] ) ) r--;
                        p[++r]=now; int tmp; scanf( "%d",&tmp ); n+=tmp; 
                }
                else
                {
                        int x,y; scanf( "%d%d",&x,&y );
                        b+=x; k+=y;
                }
                while ( r>1 && calc( p[r] )>=calc( p[r-1] ) ) r--;
                printf( "%lld %lld\n",p[r].first+1,calc(p[r]) );
        }
}