タイトル
バックグラウンド
毎年恒例のバラエティー番組「中国の新しいコードは」始まりました。プログラマになることのZayidの子供の頃の夢、彼はそれが彼らの舞台ショーだと思った、と彼はサインアップすることを躊躇しませんでした。
説明
Zayidが正常にオーディションを通過した何百回も、次のリンクはブラインドメンターを選択し、この段階でのルールはこれです:
合計\(N \)出場(から数え\(1 \)する\(N \)コードを記述し、その夢を紹介する一人当たり)。そして、すべてのインストラクターのこれらの選手をランク付け。その後の問題を回避するためには、の規定のために結ば状況が存在していません。
同時に、各プレイヤーは独立の合計に、ボランティアのフォームに必要事項を記入します\(m個\) (から数えメンター\(1 \)する(m個\)を\評価します)。ボランティアは、テーブルの上に含まれていた\(M \)ファイルのボランティア。自主各ファイルについて、選手たちが埋めるまで許可されている\(C \)メンターを、各家庭教師は(時間を埋めるために、各プレイヤー次第ですメンターの一部が許可されているを放棄します)。
双方の後の仕事は、入学プロセスを完了しています。各教師の最大数は、いくつかのプレイヤーが高いボランティアを有していてもよく、あるいはボランティアのすべてを満たすことができなかったことを意味し、自分の一族を、持っています。上のプログラムグループ「前\は(私は\)以下の入場の結果は、最適な名前を定義作られました」:
前に\(1 \)入場料は、最適な名前を、結果の場合に限り最初の\(1 \)の名前が(最高null以外の自主的な入場料で、特に、もし最初の\(1 \)、その後、ボランティアのフォームに必要事項を記入しませんでしたプレイヤーアウト)。
前\(Iは\)入院の結果は、最適であるかの先行する場合にのみ\は、(i-1 \)最適条件下受付結果は以下の通りであった:最初の\(Iは\)自発的な理論上の最大可能である名前(具体的には、最初の場合は入場料\(私は\)ボランティアのフォームに必要事項を記入していない、またはインストラクターのすべてのボランティアチームがフルメンバーで、その後、プレイヤーアウト)。
解決策は、「会いする場合は」前に\(N \)入場結果が最適であった「」、我々は短いこのプログラムのあることができます最高。
たとえば、\(2 \)メンター\(T \)先生、\(F \)にあるすべての教師の最大数をチーム\(1 \) ;人\(2 \)をプレイヤーZayid、DuckDの内訳最初の\(1 \)、\ (2 \)の名前。次に、以下の\(3 \)ボランティアの種類は、その対応する最適受付結果を表1に示す。形成します。
最適解に対応するのみ入場結果である、上記ボランティアフォームに示すことができます。
誰もが自身の理想値がある\(S_Iを\) 、を表す\(私は\)の学生が最初になりたいビット\(S_I \)でない場合は、その後、彼は非常に動揺となり、以上の自主入場を。
今、すべてのプレイヤーは、ボランティアの形や宣伝にランクされています。偶然にも、各プレイヤーのランキングは正確に自分の番号と同じです。
すべてのプレーヤーのために、Zayidは、次の2つの質問に対する答えを知りたいです:
- 最高のシナリオでは入学、彼はいくつかの自発的入院の最初のでしょう。
- 他のプレイヤーの場合、相対的なランキングは、少なくとも彼が押されていないようにする方法を多くの台頭、変わりません。
「中国の新しいコードは」手を送信するためのコード電力であるとして、Zayidコースは、簡単にこの問題を解決します。しかし、彼は、あなたが自分の計算の正確さをテストするために、それらを超えるやりたいです。
入力
各試験点は、テストデータセットを複数含み、最初の行の\(2 \)スペースで区切られた非負整数\(T、C \)数監視、それぞれ、データセットの数、各ファイルは、ボランティア最大値を満たします。
次に、データの各セットは、データの各セットについて、順に説明します:
第\(1 \) 2つの正の整数の空間によって分離線\(N、M \) 。
\(N、Mの\)プレーヤーの数、インストラクターの数を示します。
第\(2 \) OK \(m個\)スペースで区切られた正の整数:前記第一\(Iは\)番目の整数\(B_i \) 。
\(b_i \)その数はされて(私は\)\メンターチームの数の上限を。
セクション\(3 \)する行目(N + 2 \)\線\(m個\)スペースで区切られた非負の整数:前記第一\(I + 2 \)行の左からの\( J \)の数\(A_ {I、J} \)
\(A_ {I、Jは} \) の選手の番号iがスーパーバイザ配置j個の第1番号付けされ表す\(A_ {I、J} \) のボランティア。具体的には、場合\(A_ {I、J} = 0 \)は、プレイヤが教師ボランティアフォームを記入しないことを示しています。
このセクションでは、正の数で存在しない各行が複数発生することを確実にする\(C \)(0よりも発生する可能性があり、\(C \)回)を確保しつつ、その全て\(A_ {I、J} \ leqslant M \ )。
最初の\(N + 3 \) OK \(\ N-)スペースで区切られた正の整数であり、前記第一\(Iは\)整数番目の\(S_I \)
\(S_I \は)私の理想値として、選手の数を示します。
このセクションでは、確実にする\(S_Iを\ leqslant M \) 。
\(T \のleqslant 5、m個の\ leqslant N \のleqslant 200、b_i \ leqslant N \)
出力
順次各回答データを出力します。各試験のために、出力\(2 \)行:
第\(1 \)ライン出力\(N- \)スペースで区切られた正の整数であり、前記第一\(Iは\)整数の意義:
最良のシナリオの入場では、番号が\(I \)選手は、ファイル自主入場となります。
特に、アウトプレーヤーなら、その後の数 \(M 1 + \) 。
第\(2 \)ライン出力\(N- \)スペースで区切られた負でない整数であって、前記第一\(Iは\)整数として意味番目:
数\(私は\)選手が、彼のランキング上昇を作るために必要な最小数落ち込んではありません。
具体的には、プレイヤーは、その後、数押される場合には 、\を(私は\) 。
サンプル入力
3 5
2 2
1 1
2 2
1 2
1 1
2 2
1 1
1 2
1 2
2 1
2 2
1 1
0 1
0 1
2 2
サンプル出力
2 1
1 0
1 2
0 1
1 3
0 1
ヒント
それぞれ3つのテーブルに対応するデータの3つのセットは、[タイトル]を記載しました。
最初の場合は(1 \)\のデータのセット:プレイヤーとして\(1 \)を埋めるために最初の選択肢ではないので、彼は最初の自主的な入場料であってはならない、それがイライラになります。
プレーヤー\(2 \)元ランキングは押されていないので、彼はランキングを向上させるために必要はありません。
最初の場合は\(2 \)とグループ\(3 \)データのセット:\(1 \)番の選手がランキングを改善する必要はありません。
そして、私は最初の自主的な入場になりたい\(2 \)番の選手が最初に上昇しなければならない\(1 \)そうする名前。
分析
最初の質問:
プレーヤーに代わってポイントを残し、ドットの右メンターを表し、ソース\(S \)に接続されているプレーヤーの各々、流量は1であるインストラクターあたりもシンクに\(Tの\) 、チーム数のレート制限メンターを流れます。
その後から各選手のボランティアは、プラス側に入りました
現在のプレイヤーは、現在のネットワークは、ボランティア側を保持、完全な流れをストリーミングすることができ、現在の自主を満たし、その後、次のプレイヤーにあれば、
それ以外の場合は、ボランティアの方を削除してから、次のボランティア。
2番目の質問:
多くの選手が前方に移動する方法をこの二分法。
仮定はプレーヤーのことです\(私は\)が進めるために\(X \)の前に、名前を\(IX-1 \)選手が名のボランティア依頼する最初に会うために側がプレイヤーのリストに追加(私は\)\エッジは、完全な流れかどうかを判断するために、追加の時間の完全な流れを決定するために注意を払う含まれていませんフロント\(IX-1 \)何のメンターの選手がない選手を。
コード
#include<bits/stdc++.h>
const int maxn=211,maxm=8e4+10;
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::read;
using IO::write;
int ver[maxm<<1],edge[maxm<<1],Next[maxm<<1],head[maxn<<1],len=1;
inline void add(int x,int y,int z)
{
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
ver[++len]=x,edge[len]=0,Next[len]=head[y],head[y]=len;
}
int s,t;
int dist[maxn<<1],cur[maxn<<1];
inline bool bfs()
{
std::queue<int>q;
for (int i=s; i<=t; ++i) cur[i]=head[i],dist[i]=0;
q.push(s),dist[s]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (edge[i] && !dist[y])
{
dist[y]=dist[x]+1;
if (y==t) return 1;
q.push(y);
}
}
}
return 0;
}
inline int get(int x,int low)
{
if (x==t) return low;
int tmp=low;
for (int &i=cur[x]; i; i=Next[i])
{
int y=ver[i];
if (edge[i] && dist[y]==dist[x]+1)
{
int a=get(y,std::min(tmp,edge[i]));
if (!a) dist[y]=0;
edge[i]-=a;
edge[i^1]+=a;
if (!(tmp-=a)) break;
}
}
return low-tmp;
}
typedef int iarr[maxn];
iarr match,lim,dream;
int cnt[maxn][maxn],n,m;
int a[maxn][maxn][maxn];
inline void Clear()
{
memset(match,0,sizeof(match));
memset(cnt,0,sizeof(cnt));
memset(head,0,sizeof(head));
len=1;
}
inline void InPut()
{
read(n);read(m); s=0, t=n+m+1;
for (int i=1; i<=m; ++i) read(lim[i]);
for (int i=1; i<=n; ++i)
for (int j=1,x; j<=m; ++j)
{
read(x);
if (x) a[i][x][++cnt[i][x]]=j;//第 i 位选手的第 x 个志愿是第 cnt[i][x] 个的为第 j 个导师
}
for (int i=1; i<=n; ++i) read(dream[i]);
}
inline void Solve1()
{
for (int i=1; i<=m; ++i) add(i+n,t,lim[i]);//每名导师 连向 汇点t,流量为导师战队人数的上限
for (int i=1; i<=n; ++i)
{
add(s,i,1);//源点s 连向 每名选手,流量为1
for (int j=1; j<=m; ++j)
if (cnt[i][j])
{
for (int k=1; k<=cnt[i][j]; ++k) add(i,a[i][j][k]+n,1);//每名选手 连向 志愿导师,流量为1
if (bfs())
{
get(s,i);
match[i]=j;
break;
}
else
for (int k=1,h=len; k<=cnt[i][j]; ++k,h-=2) edge[h]=edge[h^1]=0;//相当于删边操作
}
}
for (int i=1; i<=n; ++i) write(match[i] ? match[i] : m+1,i==n ? '\n' : ' ');
}
inline bool check(int x,int goal)
{
memset(head,0,sizeof(head));
len=1;
for (int i=1; i<=m; ++i) add(i+n,t,lim[i]);//开始检测
int sum=0;
for (int i=1; i<goal; ++i)
{
add(s,i,1);
if (!match[i]) ++sum;
else
for (int j=1; j<=cnt[i][match[i]]; ++j) add(i,a[i][match[i]][j]+n,1);
}
add(s,x,1);
for (int i=1; i<=dream[x]; ++i)
for (int j=1; j<=cnt[x][i]; ++j) add(x,a[x][i][j]+n,1);
while (bfs()) sum+=get(s,goal);
return sum==goal;
}
inline void Solve2()
{
for (int i=1,l,r,mid,ans; i<=n; ++i)
{
if (match[i] && match[i]<=dream[i]) ans=0;
else
{
l=1, r=i-1, ans=i;
while (l<=r)
{
mid=(l+r)>>1;
if (check(i,i-mid)) r=mid-1, ans=mid;
else l=mid+1;
}
}
write(ans,i==n ? '\n' : ' ');
}
}
int main()
{
int T,C;read(T);read(C);
while (T--)
{
Clear();
InPut();
Solve1();
Solve2();
}
IO::flush();
return 0;
}