盘点我那些因概念理解不清而TLE的题(洛谷)

1.P1073——DAG

想法:

我的想法是tarjan求强连通分量之后dfs更新每个点的maxx(卖出价)(在这个节点买入),dfs应该是O(n)的。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
#define itn int
const int N = 1e5 + 5,M = 5e5 + 5;
int dfn[N],low[N],stac[N],head[N],h[N],val[N],bel[N],dp[N],minn[N],maxx[N];
int n,m,tot,cnt,ans,top,t,idx,u,v,z;
bool vis[N];
struct edge{
    int v,next;
}e[M<<1],e2[M<<1];
inline void add(int u,int v){
    e[++tot] = (edge){v,head[u]};
    head[u] = tot;
}
inline void ad(int u,itn v){
    e2[++t] = (edge){v,h[u]};
    h[u] = t;
}
void tarjan(int u){
    low[u] = dfn[u] = ++cnt;
    stac[++top] = u;
    vis[u] = true;
    repe(i,u)
        if(!dfn[e[i].v]){
            tarjan(e[i].v);
            low[u] = min(low[u],low[e[i].v]);
        }
        else if(vis[e[i].v])
            low[u] = min(low[u],dfn[e[i].v]);
    if(low[u] == dfn[u]){
        int cur;
        idx++;
        do {
            cur = stac[top--];
            vis[cur] = false;
            bel[cur] = idx;
            minn[idx] = min(minn[idx],val[cur]);
            maxx[idx] = max(maxx[idx],val[cur]);
        } while(cur != u);
    }
}
void dfs(int u){
    vis[u] = true;
    for(int i = h[u];i;i = e2[i].next)
        if(!vis[e2[i].v]) dfs(e2[i].v);
}
void dfs2(int u,int fa){
    maxx[u] = max(maxx[u],maxx[fa]);
    ans = max(ans,maxx[u] - minn[u]);
    for(int i = h[u];i;i = e2[i].next)
        if(vis[e2[i].v] && e2[i].v != fa) dfs2(e2[i].v,u);
}
int main(){
    scanf("%d %d",&n,&m);
    rep(i,n) scanf("%d",&val[i]);
    rep(i,m){
        scanf("%d %d %d",&u,&v,&z);
        add(u,v);
        if(z > 1) add(v,u);
    }
    rep(i,n) minn[i] = INT_MAX;
    minn[0] = INT_MAX;
    tarjan(1);
    rep(i,n) repe(j,i) if(bel[i] != bel[e[j].v]) ad(bel[e[j].v],bel[i]);
    dfs(bel[n]);
    dfs2(bel[n],0);
    printf("%d",ans);
    return 0;
}

但是$80pts$,TLE两个点。

我改了建边(判重),但是调试信息告诉我这算法远不是O(n)的,卡在了dfs2。

固执的80pts!!!

“正解”:

然而,我心中想当然的的DAG是这样的……

我没有想到DAG完全可以是这样的……

这就导致一个点可以被遍历好多次,复杂度远超预期。

这样就过了

#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int i = 1;i <= n;i++)
#define repe(i,u) for(int i = head[u];i;i = e[i].next)
#define itn int
……
void dfs(int u,int fa){
    maxx[u] = max(maxx[u],maxx[fa]);
    ans = max(ans,maxx[u] - minn[u]);
    vis[u] = true;
    for(int i = h[u];i;i = e2[i].next)
        if(!vis[e2[i].v]) dfs(e2[i].v,u);
}
map < pair <int,int>,bool> mp;
……
int main(){
    read(n),read(m);
    rep(i,n) read(val[i]);
    rep(i,m){
        read(u),read(v),read(z);
        add(u,v);
        if(z > 1) add(v,u);
    }
    rep(i,n) minn[i] = INT_MAX;
    tarjan(1);
    rep(i,n) repe(j,i) if(vi[i] && vi[e[j].v] && bel[i] != bel[e[j].v] && !mp[make_pair(bel[e[j].v],bel[i])])
        ad(bel[e[j].v],bel[i]),mp[make_pair(bel[e[j].v],bel[i])] = true;
    dfs(bel[n],0);
    printf("%d",ans);
    return 0;
}

后记:

其实这样还是错的,只是数据太水。

标记vis可能无法正确更新“maxx[u] = max(maxx[u],maxx[fa]);”。

正解是拓扑排序。

2.P2375——KMP

想法:

我的想法是:先求nxt数组,然后再判断是否重叠,重叠了继续往前跳j = nxt[j]

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define ll long long
const int N = 1e6 + 5,mod = 1e9 + 7;
int nxt[N],num[N],T,n,j,ans;
char s[N];
inline void read(int &x){
    x = 0;
    int f = 1;
    char c;
    for(c = getchar();!isdigit(c);c = getchar()) if(c == '-') f = -1;
    for(;isdigit(c);c = getchar()) x = (x << 3) + (x <<1) + (c ^ 48);
    x *= f;
}
inline void write(int x){
  if(x < 0) x = -x,putchar('-');
    if(x / 10) write(x / 10);
    putchar((x % 10 + '0'));
}
void work(){
    nxt[1] = j = 0,num[1] = 1;
    n = strlen(s + 1);
    rep(i,2,n){
        while(j && s[i] != s[j+1]) j = nxt[j];
        j += s[j+1] == s[i];
        nxt[i] = j,num[i] = num[j] + 1;
    }
    ans = 1;
    rep(i,2,n){
        j = nxt[i];
        while(j > i >> 1) j = nxt[j];
        ans = ((ll)ans * (num[j] + 1)) % mod;
    }
    write(ans),putchar('\n');
}
int main(){
    read(T);
    while(T--){
        scanf("%s",s + 1);
        work();
    }
    return 0;
}

50ptsTLE QAQ

正解:

看了题解,好像没什么不同,除了我的第二个循环里的j = nxt[j]好像比它快一些?

事实打脸:改成题解那样就过了。 

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define ll long long
const int N = 1e6 + 5,mod = 1e9 + 7;
int nxt[N],num[N],T,n,j,ans;
char s[N];
inline void read(int &x){
	x = 0;
	int f = 1;
	char c;
	for(c = getchar();!isdigit(c);c = getchar()) if(c == '-') f = -1;
	for(;isdigit(c);c = getchar()) x = (x << 3) + (x <<1) + (c ^ 48);
	x *= f;
}
inline void write(int x){
  if(x < 0) x = -x,putchar('-');
	if(x / 10) write(x / 10);
	putchar((x % 10 + '0'));
}
void work(){
	nxt[1] = j = 0,num[1] = 1;
	n = strlen(s + 1);
	rep(i,2,n){
		while(j && s[i] != s[j+1]) j = nxt[j];
		j += s[j+1] == s[i];
		nxt[i] = j,num[i] = num[j] + 1;
	}
	ans = 1,j = 0;
	rep(i,2,n){
		while(j && s[i] != s[j+1]) j = nxt[j];//注意这两行
		j += s[j+1] == s[i];//
		while(j > i >> 1) j = nxt[j];
		ans = ((ll)ans * (num[j] + 1)) % mod;
	}
	write(ans),putchar('\n');
}
int main(){
	read(T);
	while(T--){
		scanf("%s",s + 1);
		work();
	}
	return 0;
}

百思不得其解

TLE的数据点:输入1e5个'a'

问了Cindy_Li学姐,她说:“你每次都要从i跳到i/2,给你一个aaaaaaaaa,你的做法就会被卡到n^2”

我输出了调试信息……果真如此

我只看到循环开始时j的值是一样的,但题解相当于在后面把j更新了,不用每次跳那么多步。这正是KMP的精髓所在啊,用失配信息计算答案

对kmp的理解加深了好多,考虑要全面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值