近期总结 - 图论相关 cf 936B / cf 949C /ZOJ 3999

/看了Irene的博文,深受感动,决定也来写点什么东西纪念一下/


/昨天给女队上课讲了bfs也不知道自己弄懂了没有/

最近codeforces rating忽上忽下,终于还是回来啦。

也越来越觉得图论非常有意思,虽然我还在图论外围摸爬滚打,但看起来真的很有趣呀。


下面直接上题:

Codeforces 936B(1B/2D)


第二次打div1,只做了一题就跑路了,本来愉快pretest passed了这题之后开心地睡觉了(大概只是比赛开始40分钟,因为C完全不会做)

但是刚躺下突然想起,哦!我好像错了!

幸运的是,即使只过了一题,还是只-8(rating1912)

昨天修程序才发现错得真是非常彻底。

题意:给定一张有向图,问能否找出一条从顶点开始到一个出度为0的点,路径的长度为偶数。

若有,输出win并还原路径

若不为偶数,如果能到一环,则输出Draw,否则输出Lose。


自己写了傻傻的dfs老半天,缝缝补补问题越来越多,拆东墙补西墙,

意外地还是过了,把一个点拆成两个点,偶数过和奇数过是两种情况。

(感谢栗酱让我明白了判断有没有环竟然是判断在不在栈内的(图论白学.jpg))

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mem(a) memset(a,1,sizeof(a))
typedef long long ll;
typedef pair<int,int> pii;

const int mn=2e5+5;

int n,m;
int s;
vector<int> g[mn];
int vis[mn][2];
int p[mn][2];
int ins[mn][2];
int ans,choose;

vector<int> v;
void dfs(int x,int k){
	ins[x][k%2]=1;
	vis[x][k%2]=k;
	for(int i:g[x]){
		if (ins[i][(k+1)%2]) ans=max(1,ans);
		if (vis[i][(k+1)%2]) continue;
        else{
			p[i][(k+1)%2]=x;
            dfs(i,k+1);
        }
	}
	if (g[x].size()==0&&k%2==0){
		ans=k;
		choose=x;
	}
	ins[x][k%2]=0;
}

int main() {
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		int u;
		scanf("%d",&u);
		for(int j=1;j<=u;j++){
			int v;
			scanf("%d",&v);
			g[i].pb(v);
		}
	}
	scanf("%d",&s);
	dfs(s,1);
    if (ans==1) puts("Draw");
    else if (!ans) puts("Lose");
    else{
		v.clear();
        puts("Win");
        int now=choose;
        v.pb(now);
        while(ans>1){
			//printf("%d ",now);
            now=p[now][ans%2];
            ans--;
            v.pb(now);
        }
        reverse(v.begin(),v.end());
		for(int i:v) printf("%d ",i);
    }
	return 0;
}

啊啊,总之非常神奇,就ac了。


第三场div1

对于Codeforces 949C(1C),又是经典图论题。

题目贼长,简直窒息。又正好被上一题找规律打的头晕目眩。

然后这把就正式掉出div1了,弘毅大大却借这场成功上紫,从此阴阳两隔(弥天大雾)

题意:经过复杂变换后,可以得到一张有向图,任意在有向图上选择至少一个点,但必须使所有该点可达的点也全部被选中。

问最少能选几个点(并给出点集)。



又是自己写了半天dfs发现,哇,怎么怎么写怎么错。

突然想起原来还有强连通分量的板子,发现我竟然手写不出来。

板子看了看都有点忘记了,拿出了以前的几个程序看了看,哇,真神奇啊。

(学的比忘得快系列)

然后终于AC了。

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mem(a) memset(a,0,sizeof(a))
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;

const int mn=1e5+5;

int n,m,h;
int a[mn],out[mn];
vector<pii> e;
vector<int> g[mn],v,ans;
int pre[mn],lowlink[mn],sccno[mn],dfsc,sccc;
vector<int> scc[mn];
stack<int> s;

void dfs(int u){
	pre[u]=lowlink[u]=++dfsc;
	s.push(u);
	for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if (!pre[v]){
			dfs(v);
            lowlink[u]=min(lowlink[u],lowlink[v]);
        }else if (!sccno[v]){
        	lowlink[u]=min(lowlink[u],pre[v]);
		}
	}
	if (lowlink[u]==pre[u]){
		sccc++;
		while(1){
			int x=s.top();s.pop();
			sccno[x]=sccc;
			if (x==u) break;
		}
	}
}

void find_scc(){
	dfsc=sccc=0;
    mem(sccno);mem(pre);
    for(int i=1;i<=n;i++) if(!pre[i]) dfs(i);
}

int main() {
	scanf("%d%d%d",&n,&m,&h);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
	for(int i=1; i<=m; i++) {
		int u,v;
		scanf("%d %d",&u,&v);
		if (u>v)
			swap(u,v);
		e.pb(pii(u,v));
	}
	sort(e.begin(),e.end());
	e.erase(unique(e.begin(),e.end()),e.end());
	for(int i=0; i<e.size(); i++) {
		int u=e[i].first,v=e[i].second;
		if ((a[u]+1)%h==a[v])
			g[u].pb(v);
		if ((a[v]+1)%h==a[u])
			g[v].pb(u);
	}
	/*for(int i=1; i<=n; i++) {
		printf("%d:",i);
		for(int j:g[i])
			printf("%d ",j);
		puts("");
	}*/
    find_scc();
    for(int i=1;i<=n;i++){
        for(auto j:g[i]){
            if (sccno[i]!=sccno[j]){
				out[sccno[i]]=1;
            }
        }
        scc[sccno[i]].pb(i);
    }
    for(int i=1;i<=sccc;i++){
        if (!out[i]&&(ans.empty()||ans.size()>scc[i].size())) ans=scc[i];
    }
    //answer
	printf("%d\n",ans.size());
	for(int i:ans)
		printf("%d ",i);
	return 0;
}


幸运的是昨晚(今天早上codeforces round470)题目非常好,我又回来了,div1!

Trie树竟然还记得!佩服我自己。

2B坚信水题一条路可以走到底暴力卡常竟然过了。

以后有空学学字符串,在字符串方面简直小白了。


今天还和团队做了ZOJ Monthly January,2018

题目真的难到窒息了,发现我的队友真是强到爆炸,除了写模拟题只能划水的我……

写了个多项式洛必达法则就全程划水了……

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10005;
#define pb push_back
typedef pair<int,int> pii;
typedef long long ll;

char s[N],s1[N];
int n,m;
ll x0;
ll su,sd;

vector<pii> u,d;//up / down

char *p;

ll gcd(ll a,ll b){
    return !b?a:gcd(b,a%b);
}

pii getu(){
    int u=1,v=0;
    if (*p=='+') p++;
    else if (*p=='-') u*=-1,p++;
    if (*p!='x'){
        u*=(*p)-'0';
        p++;
    }
    if (*p=='+'||*p=='-'||*p==0) return pii(u,0);
    else {
        assert(*p=='x');
        p++;
        v++;
        if (*p=='^'){
            p++;
            v=*p-'0';
            p++;
        }
    }
    return pii(u,v);
}

void luobida(){
    vector<pii> t;
    for(pii i:u) {
        i.first*=i.second;
        if(i.second) t.pb(pii(i.first,i.second-1));
    }
    u=t;
    t.clear();
    for(pii i:d) {
        i.first*=i.second;
        if(i.second) t.pb(pii(i.first,i.second-1));
    }
    d=t;
}

ll px0[10];

void getsum(){
    su=sd=0;
    for(pii &i:u){
        su+=i.first*px0[i.second];
    }
    for(pii &i:d){
        sd+=i.first*px0[i.second];
    }
}

int main() {
    int t; scanf("%d", &t);
    while (t--) {
        u.clear();d.clear();
        scanf("%s%s",s,s1);
        p=s;
        while(*p!=0) u.pb(getu());
        p=s1;
        while(*p!=0) d.pb(getu());
        scanf("%lld",&x0);
        px0[0]=1;
        for(int i=1;i<=9;i++) px0[i]=px0[i-1]*x0;
        getsum();
        while(!sd&&!su){
            luobida();
            getsum();
        }
        if (!sd){
            puts("INF");
            continue;
        }
        if (!su){
            puts("0");
            continue;
        }
        ll g=gcd(su,sd);
        su/=g,sd/=g;
        if (sd<0){
            su*=-1;
            sd*=-1;
        }
        if (sd==1) printf("%lld\n",su);
        else printf("%lld/%lld\n",su,sd);
    }
}

想好好学学图论和工具了,

发现原来自己真的什么都不懂啊……


加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值