題解
好題!!!
競賽圖就是把一個無向完全圖的所有邊定向
這道題其實是比較簡單的,考試的時候一直沒看到刪除k個欽定點圖是一個DAG的條件,就一直沒有思路
我們可以把欽定點設爲A集合,其他的點設爲B集合
顯然,如果A集合有環,那麼肯定無解
根據條件,B集合也是DAG
所以A、B集合都是DAG
我們可以考慮一下他們的拓撲序
設A->B的有向邊爲x類邊,B->A的爲y類邊
對於B集合的一個點u,A集合的所有點都會與它有連邊(因爲是有向完全圖嘛),或爲x類邊,或爲y類邊
把這些連邊按照它們對應的A集合的點的拓撲序來排序,如:xxyyxyyxyyxxyyx
可以發現如果出現了y類邊,再出現x類邊,那麼必定會構成環u->Ai->Ai+1->...->->Aj->u (假設Ai對應的y類邊,Aj對應x類邊)
由於A集合不能被刪除,所以我們只好刪掉B集合的點u
那麼剩下的B集合的點的對應A集合連邊類型序列就一定是xxx...xyyyy...y
考慮到這種情況
如果B集合中靠後點的y序列範圍與B集合中靠前點的x序列範圍有交集
那麼它們也會構成環
於是我們可以對於B集合裏的每一個數都存一下:從左到右第一次有x變成y的位置
然後我們發現這就是一個經典的最長不下降子序列問題
DP一下就可以了
代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
inline int gi()
{
char c;int num=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num;
}
#define N 2005
int fir[N],to[N*N],nxt[N*N],cnt,d[N];
bool flg[N];
int e[N][N];
int a[N],cnta,b[N],cntb;
void adde(int a,int b){to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;d[b]++;}
queue<int> q;
int f[N],cntf,dp[N];
int main()
{
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
int n,k,i,j,u,v,p,x;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++)for(j=1;j<=n;j++)e[i][j]=gi();
for(i=1;i<=k;i++)scanf("%d",&x),flg[x]=1;
for(i=1;i<=n;i++)if(flg[i])
for(j=1;j<=n;j++)if(flg[j])
if(e[i][j])adde(i,j);
for(i=1;i<=n;i++)if(flg[i]&&!d[i])q.push(i);
while(!q.empty()){
u=q.front();q.pop();a[++cnta]=u;
for(p=fir[u];p;p=nxt[p])
if(!(--d[v=to[p]]))q.push(v);
}
if(cnta<k){printf("impossible\n");return 0;}
memset(fir,0,sizeof(fir));cnt=0;
for(i=1;i<=n;i++)if(!flg[i])
for(j=1;j<=n;j++)if(!flg[j])
if(e[i][j])adde(i,j);
for(i=1;i<=n;i++)if(!flg[i]&&!d[i])q.push(i);
while(!q.empty()){
u=q.front();q.pop();b[++cntb]=u;
for(p=fir[u];p;p=nxt[p])
if(!(--d[v=to[p]]))q.push(v);
}
for(i=1;i<=cntb;i++){
int tmp=cnta+1;bool ff=0;
for(j=1;j<=cnta;j++){
if(e[b[i]][a[j]]){
if(j==1||e[a[j-1]][b[i]]){
if(tmp==cnta+1)tmp=j;
else{ff=1;break;}
}
}
}
if(e[a[cnta]][b[i]]&&tmp!=cnta+1)ff=1;
if(!ff)f[++cntf]=tmp;
}
int mx=0;
for(i=1;i<=cntf;i++){
dp[i]=1;
for(j=1;j<i;j++)
if(f[j]<=f[i])
dp[i]=max(dp[j]+1,dp[i]);
mx=max(mx,dp[i]);
}
if(n-k-mx>=k)printf("impossible\n");
else printf("%d",n-k-mx);
}
T2用一篇博客單獨來講
T3:
題解
這道題在考試的時候本來有一些思路的,但是把大量時間花在了T2上面,所以就沒繼續深入思考
很明顯,最後的序列只包含0、1,我們只需要維護0的位置就可以了
而0的位置是有規律的(就像上面的題解),每減少一下當前的a[i],就會把最近的一個0拉近一步
然後就可以用一個單調棧來維護0的位置就可以了
代碼:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 20000005
int n,top;
char s[N];
int a[N],stk[N];
int main()
{
freopen("simulate.in","r",stdin);
freopen("simulate.out","w",stdout);
int i,tmp;
scanf("%s",s+1);
n=strlen(s+1),stk[++top]=0;
for(i=1;i<=n;++i){
int x=s[i]-48+a[i];s[i]='1';
while(x>1){
if(stk[top]==i-1){
x-=2;
a[i+1]++;
top=max(1,top-1);
}
else if(!stk[top]){
stk[++top]=1;
x--;
a[i+1]++;
}
else{
tmp=min(i-1-stk[top],x-1);
stk[top]+=tmp;
x-=tmp;
a[i+1]+=tmp;
}
}
if(!x)stk[++top]=i;
}
while(top)s[stk[top--]]='0';
puts(s+1);
}