CF1137 C. Museums Tour

一般来说的正常思路:看到有向图的第一思路都是缩点(但是要分析一波证明强联通分量中的个体可以拼凑成整体,一般都是边和点可以经过无数次然后贡献只算一次这种类型)\(DAG\)上面DP.

这道题第一眼看上去比较麻烦,因为这个时间限制比较恶心的样子.

我们考虑拆点,将一个点拆成\(d\)个,\((i,j)\)表示第\(i\)个点在第\(j\)天的联通情况

这样的话,我们在缩点的时候,就统计一下每一个强联通分量内有多少点在其对应的博物馆的开放时间内(注意去重)

这样的话,我们貌似就得到了每个强联通分量的点权

剩下的貌似就是一个\(DAG\)上的最长链问题了.

但是我们好像没有考虑一个点在两个或多个强联通分量都有贡献的情况

很幸运这是不需要考虑的.因为不可能出现上述情况

为什么?

我们说假设存在这样一次路径,满足\((i,x) ->(i,y)\)即从\(i\)点的第\(x\)天能够到达第\(i\)个点的第\(y\)天,我们设\(\Delta d = y - x\)(默认\(y\)大于\(x\))

那么一定存在\((i,0)->(i,d - 1)\)

以此类催,我们发现从\(y\)也可以到达\(x\),那么他们必在同一强联通分量内

然后就结束了

我栈用了存边数组的变量\(tot\)导致疯狂RE

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 3;
const int M = 61;
struct edge{
    int to;
    int nxt;    
}e[N * M];
vector <int> G[N * M];
int val[N * M],head[N * M];
char s[N][M];
int tot;
int cnt,n,m,d,idx,top;
bool insta[N * M];
int sta[N * M];
int dfn[N * M],belong[N * M],low[N * M];
int f[N * M];
//int can[N];
bool flag[N * M];
int out[N * M];
//inline char nc(){
//    #define SIZE 100000
//    static char buf[SIZE],*p1 = buf+SIZE,*pend = buf+SIZE;
//    if(p1 == pend){
//        p1 = buf;pend = buf+fread(buf,1,SIZE,stdin);
//        if(p1 == pend) return -1;
//    }
//    return *p1++;
//    #undef SIZE
//}
inline void read(int &x){
    x = 0;int flag = 0;
    char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') flag = 1;
        ch = getchar();
    }
    while(isdigit(ch)){
        x = (x<<1) + (x<<3) + (ch^'0');
        ch = getchar();
    }
    if(flag) x = -x;
}
inline void add(int x,int y){
    e[++tot].to = y;
    e[tot].nxt = head[x];
    head[x] = tot;  
}
inline void tarjan(int x){
//  cout << x  << endl;
    dfn[x] = low[x] = ++idx;
    sta[++top] = x; 
    insta[x] = 1;
    for(int i = head[x];i;i = e[i].nxt){
        int y = e[i].to;
        if(!dfn[y]){
            tarjan(y);
            low[x] = min(low[x],low[y]);
        }
        else if(insta[y]) low[x] = min(low[x],dfn[y]);
    }
    if(dfn[x] == low[x]){
        int k;cnt++;
        int fo = top;
        do{
            //tot = -1;
            k = sta[top--];
            insta[k] = 0;
            belong[k] = cnt;
            if(s[k / d][k % d] == '1' && flag[k / d] == 0) {val[cnt]++;flag[k / d] = 1;}
        //  if(k % d == 0) can[cnt] = 1;
        }while(k != x);
        for(int i = fo;i != top;--i) flag[sta[i] / d] = 0;
    }
}
inline int tops(){
    queue <int> q;
//  q.push(belong[0]);
//  cout <<in[belong[0]] << endl;
    for(int i = 1;i <= cnt;++i) f[i] = val[i];
    for(int i = 1;i <= cnt;++i) if(!out[i]) f[i] = val[i],q.push(i);
    while(!q.empty()){
        int k = q.front();q.pop();
        for(int i = 0;i < (int)G[k].size();++i){
            int y = G[k][i];
            f[y] = max(f[y],f[k] + val[y]);
            out[y]--;
            if(!out[y]) q.push(y);
        }
    }
//  for(int i = 1;i <= cnt;++i) cout << f[i] << ' ';cout << endl; 
    return f[belong[0]];
}
int main(){
//  printf("%d\n",sizeof(e) / 1024 / 1024);
    read(n),read(m),read(d);
    for(int i = 1;i <= m;++i){
        int x,y,z;
        read(x);read(y);x--,y--;
        for(int j = 0;j < d;++j)
        add(x * d + j,y * d + ((j + 1) % d));
    }
    for(int i = 0;i < n;++i) scanf("%s",s[i]);
    if(n == 1 && m == 0){
        cout << s[0][0];
        return 0;
    }
//  if(n == 99998){cout << "GG";return 0;}
    for(int i = 0;i < n * d;++i)
    if(!dfn[i]) tarjan(i);
//for(int i = 0;i < n * d;++i) printf("%d ",belong[i]);puts("");
//      cout << cnt << endl;    
//  for(int i = 1;i <= cnt;++i) cout << val[i] << " ";cout << endl; 
    for(int i = 0;i < n * d;++i){
        for(int j = head[i];j;j = e[j].nxt){
            int y = e[j].to;
            if(belong[i] != belong[y]) G[belong[y]].push_back(belong[i]),out[belong[i]]++;//printf("%d %d\n",belong[i],belong[y]);
        }
    }
    //cout << cnt << endl;
//  for(int i = 1;i <= cnt;++i){
//      printf("now:%d: ",i);
//      for(int j = 0;j < G[i].size();++j){
//          printf("%d ",G[i][j]);  
//      }
//      puts("");
//  }
    printf("%d\n",tops());
    return 0;   
}