Jzoj5452【NOIP2017提高A组冲刺11.5】轰炸

227 篇文章 3 订阅
153 篇文章 0 订阅
战狂在《魔方王国》中对小奇的城市进行轰炸,每轮轰炸不能使间谍通过地道逃至已轰炸城市。题目描述导致多数人误解,实际解题策略为使用Tarjan算法进行强联通分量压缩,结合动态规划求解最少轰炸轮数。由于递归栈溢出,采用汇编扩大栈容量。距离NOIP比赛还有几天,提醒参赛者注意不要在比赛中尝试此类技巧。
摘要由CSDN通过智能技术生成

战狂也在玩《魔方王国》。他只会征兵而不会建城市,因此他决定对小奇的城市进行轰炸。
小奇有n 座城市,城市之间建立了m 条有向的地下通道。战狂会发起若干轮轰炸,每轮可以轰炸任意多个城市。
每座城市里都有战狂部署的间谍,在城市遭遇轰炸时,它们会通过地下通道撤离至其它城市。非常不幸的是,在地道里无法得知其它城市是否被轰炸,如果存在两个不同的城市i,j,它们在同一轮被轰炸,并且可以通过地道从城市i 到达城市j,那么城市i 的间谍可能因为撤离到城市j 而被炸死。为了避免这一情况,战狂不会在同一轮轰炸城市i 和城市j。
你需要求出战狂最少需要多少轮可以对每座城市都进行至少一次轰炸。

此题好坑啊,隔了一天才切掉

我们好多人看错题了qwq,题目有一句诡异的话:

并且可以通过地道从城市i 到达城市j

于是我们70%的人理解成了:i,j有连边

而不是:i可以通过地道到达j

好吧看对了这题就是一个tarjan缩点+最长路dp而已。。。

嗯上来就可以写一发

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
using namespace std;
struct Edge{ int v,nt; } G[N];
int h[N],dfn[N],low[N],col[N],f[N],n,m,cnt=0;
vector<int> G2[N]; int st[N],top=0,clk=0,Col,sz[N];
inline void adj(int x,int y){ G[++cnt]=(Edge){y,h[x]}; h[x]=cnt; }
void tarjan(int x){
	dfn[x]=low[x]=++clk; 
	st[++top]=x;
	for(int v,i=h[x];i;i=G[i].nt)
		if(!dfn[v=G[i].v]){
			tarjan(v); low[x]=min(low[x],low[v]);
		} else if(!col[v]) low[x]=min(low[x],dfn[v]);
	if(low[x]==dfn[x]){
		++Col;
		do { col[st[top]]=Col; sz[Col]++; } while(st[top--]!=x);
	}
}
int dp(int x){
	if(~f[x]) return f[x]; f[x]=0;
	for(int i=0,z=G2[x].size();i<z;++i) f[x]=max(f[x],dp(G2[x][i]));
	f[x]+=sz[x]; return f[x];
}
int main(){
	freopen("bomb.in","r",stdin);
	freopen("bomb.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int x,y,i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		adj(x,y);
	} memset(f,-1,sizeof f);
	for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;++i)
		for(int j=h[i];j;j=G[j].nt)
			if(col[i]!=col[G[j].v]) G2[col[i]].push_back(col[G[j].v]);
	for(int i=1;i<=n;++i) if(!~f[i]) f[0]=max(f[0],dp(i));
	printf("%d\n",*f);
}
加上了O3这也才95分!

当然啦,1BW层递归在OJ上肯定要爆栈的

于是,懒得打人工栈,就找了一些奇技淫巧:C++如何扩大

有兴趣可以点击黑块的链接看看,下面就把用汇编扩栈的code放上来

(还有几天就noip了请勿比赛中作死而且noip不卡栈)

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<vector>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#define N 1000010
using namespace std;
extern int main2(void) __asm__ ("main2");  
struct Edge{ int v,nt; } G[N];
int h[N],dfn[N],low[N],col[N],f[N],n,m,cnt=0;
vector<int> G2[N]; int st[N],top=0,clk=0,Col,sz[N];
inline void adj(int x,int y){ G[++cnt]=(Edge){y,h[x]}; h[x]=cnt; }

void tarjan(int x){
	dfn[x]=low[x]=++clk; 
	st[++top]=x;
	for(int v,i=h[x];i;i=G[i].nt)
		if(!dfn[v=G[i].v]){
			tarjan(v); low[x]=min(low[x],low[v]);
		} else if(!col[v]) low[x]=min(low[x],dfn[v]);
	if(low[x]==dfn[x]){
		++Col;
		do { col[st[top]]=Col; sz[Col]++; } while(st[top--]!=x);
	}
}
int dp(int x){
	if(~f[x]) return f[x]; f[x]=0;
	for(int i=0,z=G2[x].size();i<z;++i) f[x]=max(f[x],dp(G2[x][i]));
	f[x]+=sz[x]; return f[x];
}
int main2(){
	scanf("%d%d",&n,&m);
	for(int x,y,i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		adj(x,y);
	} memset(f,-1,sizeof f);
	for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;++i)
		for(int j=h[i];j;j=G[j].nt)
			if(col[i]!=col[G[j].v]) G2[col[i]].push_back(col[G[j].v]);
	for(int i=1;i<=n;++i) if(!~f[i]) f[0]=max(f[0],dp(i));
	printf("%d\n",*f); exit(0);
}
int main(){
	freopen("bomb.in","r",stdin);
	freopen("bomb.out","w",stdout);
	int size = 256 << 20;  // 256Mb  
    	char *p = (char *)malloc(size) + size;  
    	__asm__ __volatile__(  
        "movq  %0, %%rsp\n"  
        "pushq $exit\n"   
        "jmp main2\n"  
        :: "r"(p)); 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值