20200223省選模擬賽 總結

 

 

 

題解

好題!!!

競賽圖就是把一個無向完全圖的所有邊定向

這道題其實是比較簡單的,考試的時候一直沒看到刪除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);
}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章