盘点我那些因概念理解不清而 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 的理解加深了好多,考虑要全面

### 如何在洛谷平台上评测CSP真 洛谷是一个广泛使用的在线评测平台,支持多种编程语言以及算法竞赛目练习。对于CSP(中国计算机学会主办的软件能力认证)的相关真,在洛谷上可以找到对应的目并进行提交和测评。 #### 1. 寻找对应洛谷上有大量的历年CSP真收录,并按照年份、难度分类整理好。可以通过以下方式查找目标目: - 使用洛谷首页的搜索框输入关键词,例如“CSP-J 2024”或者具体目名称如“地图探险”,即可快速定位到相关目页面[^1]。 - 进入洛谷的比赛专区或单功能,这里通常会有专门针对CSP系列考试的专训练集锦[^2]。 #### 2. 提交代码前准备 在正式提交之前,请确保已经理解清楚目描述中的所有细节部分,包括但限于输入输出格式说明、数据范围限制条件等重要信息。另外还需要注意所选编程环境是否匹配官方标准版本要求,比如C++可能涉及同编译器选项设置等问[^3]。 ```cpp #include <bits/stdc++.h> using namespace std; int main(){ int n; cin>>n; //读取变量数量 vector<int> a(n); for(auto &x:a){ cin>>x;//依次读入数组元素值 } sort(a.begin(),a.end()); //对数组从小到大排序 long long sum=0LL; for(int i=0;i<n;++i){ sum += abs((long long)a[i]- (long long)(i+1)); } cout<<sum<<endl; //最终结果输出 } ``` 上述示例展示了处理一组整数并通过计算得出某个特定数值的过程。此代码片段仅作为演示用途,并直接关联任何具体的CSP试解答逻辑。 #### 3. 正确完成代码编写与测试 当明确了需求之后就可以着手实现解决方案了。务必先自行验证程序正确性再上传至平台检测。利用样例给出的数据手动运行调试有助于发现潜在错误之处。 #### 4. 在线提交流程 登录账号后访问指定目链接地址进入答界面。填写完毕源文件内容保存确认无误后再点击“提交”。等待几秒钟便能得到反馈报告,其中包含了通过率百分比以及其他统计指标情况。 如果遇到WA(Wrong Answer),TLE(Time Limit Exceeded)之类的提示,则需重新审视自己的思路是否存在缺陷或者是性能瓶颈方面有待优化空间。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值