首页 > 其他分享 >P9041 [PA2021] Fiolki 2

P9041 [PA2021] Fiolki 2

时间:2024-08-29 21:52:39浏览次数:10  
标签:int rep P9041 Poly tim Fiolki vec PA2021 define

简要题意可以去看其他题解。

前置知识:LGV 引理。

看到这道题我们先考虑该怎么判定 \(f(l,r)\) 是否等于 \(x\)。看完题面后很难不让人想到 LGV 引理(不相交路径,起点集 \(S\) 和终点集 \(T\))。但是 LGV 引理是用于计数的,放在这里似乎并不好用。但是我们可以 注意到,只要没有合法情况,那么矩阵行列式的结果一定为 \(0\),而如果有合法情况,我们给每个边赋一个在 \([1,P-1]\) 内的权值,其中 \(P\) 表示一个大质数,那么如果有合法情况,则行列式的值有极大概率不为 \(0\)。

那么现在判定 \(f(l,r)=k\) 是简单的。但是怎么对于 \(f(l,r)=x\) 判定呢?我们考虑矩阵的条件是什么。

行列式的基本性质告诉我们,如果一个矩阵存在两行(列)成比例则 \(\det(A)=0\)。这告诉我们,如果这个矩阵存在线性相关,那么行列式结果一定是零。所以 \(f(l,r)=x\) 当且仅当矩阵的矩阵的秩等于 \(x\)。

那么我们可以套路的想到扫描线,枚举每个 \(r\),对于每个 \(x\) 求最大的 \(l\) 满足 \(f(l,r)=x\),然后我们考虑使用线性基,贪心的对于每个基底选择出现时间最晚的基底,然后就可以解决本题了!

时间复杂度 \(\mathcal{O}(nk^2+mk)\)。

代码:

#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define id(x, y) ((x - 1) * n + y)
#define eb emplace_back
#define inf 1000000050
#define linf 10000000000000000
#define pii pair <int, int>
#define ls (p << 1)
#define rs (ls | 1)
using namespace std;
constexpr int N = 2e6 + 5, M = 50 + 5, P = 1042702009;
typedef long long ll;
inline ll rd () {
  ll x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) { if (ch == '-') f = -1; ch = getchar (); }
  while (isdigit (ch)) { x = (x << 1) + (x << 3) + ch - 48; ch = getchar (); }
  return x * f;
}      
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}
int n, m, k;
int deg[N];
vector <pii> e[N];
class Poly {
  public:
    int c[M];
    friend Poly operator + (const Poly& a, const Poly& b) {
      Poly c;
      rep (i, 1, 50) c.c[i] = (a.c[i] + b.c[i]) % P;
      return c;
    }
    friend Poly operator - (const Poly& a, const Poly& b) {
      Poly c;
      rep (i, 1, 50) c.c[i] = (a.c[i] - b.c[i]) % P;
      return c;
    }
    friend Poly operator * (const Poly& a, const int& b) {
      Poly c;
      rep (i, 1, 50) c.c[i] = a.c[i] * b % P;
      return c;
    }
} f[N];
void topo () {
  queue <int> q;
  rep (i, 1, k) {
    f[i].c[i] = 1;
  }
  rep (i, 1, n) if (! deg[i]) q.push (i);
  while (! q.empty ()) {
    int u = q.front ();
    q.pop ();
    for (auto p : e[u]) {
      int v = p.first, w = p.second;
      f[v] = f[v] + f[u] * w;
      if (! -- deg[v]) q.push (v);
    }
  }
}
int tim[N];
Poly vt[N];
void insert (Poly x, int t) {
  rep (i, 1, k) {
    if (! x.c[i]) continue;
    if (! tim[i]) {
      tim[i] = t, vt[i] = x;
      return ;
    } else {
      if (tim[i] < t) swap (tim[i], t), swap (vt[i], x);
      int div = qpow (vt[i].c[i], P - 2) * x.c[i] % P;
      rep (j, 1, k) (x.c[j] -= vt[i].c[j] * div) %= P;
    }
  }
}
vector <int> vec;
int ans[M];
signed main () {
  mt19937 rnd (time (NULL));
  n = rd (), m = rd (), k = rd ();
  rep (i, 1, m) {
    int u = rd (), v = rd ();
    e[u].eb (pii (v, rnd () % (P - 1) + 1)); ++ deg[v];
  }
  topo ();
  rep (i, k + 1, n) {
    insert (f[i], i);
    rep (j, 1, k) if (tim[j]) vec.eb (tim[j]);
    vec.eb (k), vec.eb (i);
    sort (vec.begin (), vec.end (), greater <int> ());
    for (int j = 0; j + 1 < vec.size (); ++ j) ans[j] += vec[j] - vec[j + 1];
    vec.clear ();
  }
  rep (i, 0, k) printf ("%lld\n", ans[i]);
}

标签:int,rep,P9041,Poly,tim,Fiolki,vec,PA2021,define
From: https://www.cnblogs.com/lalaouyehome/p/18387617

相关文章

  • 题解:P9041 [PA2021] Fiolki 2
    \(\text{Link}\)题意给定一个\(n\)个点\(m\)条边的DAG,定义\(f(l,r)\)表示最多选取多少条不相交路径\((s_i,t_i)\)满足\(s_i\in[1,k],t_i\in[l,r]\),其中不能有任意一点同时在两条选出的路径上。对\(\forallx\in[0,k)\)求出有多少\([l,r]\sube(k,n]\)使得\(f(l,r)......
  • P8386 [PA2021] Od deski do deski
    P8386[PA2021]Oddeskidodeski挺奇怪的一个转移式,还是套路看少了一开始想到的是分治,像卡特兰数那样求但是发现两端相同这个限制的去重不好处理,没法划分子问题就比如样例的\(4~2\),显然包含\(1~1~2~2\)这种情况当我们求\(6~2\)时,如果划分成\(4+......
  • [PA2021] Wystawa
    [PA2021]Wystawa牛逼啊喔趣。题意给定长度为\(n\)的序列\(a,b\)。你需要构造一个序列\(c\),构造方法为:选择\(k\)个\(i\),令\(c_i\leftarrowa_i\)。对于其他\(i\),令\(c_i\leftarrowb_i\)。求序列\(c\)的最大子段和的最小值,并给出一种方案。Sol感觉最小......
  • P8386 [PA2021] Od deski do deski 题解
    显然是一道计数dp。dp状态应该是最难的一部分了,个人认为这种状态设计得比较巧妙。如果像我刚开始一样设\(dp_{i,j}\)表示序列中一共有\(i\)个数,序列最后一个数为\(j\)的合法方案数的话,那么方程就会变得很不好转移,因为我们不知道当前的\(j\)和之前的某些数能不能匹配上,......
  • [PA2021] Poborcy podatkowi
    令\(dp_{x,d}\)表示\(x\)子树内现在根结点上挂着的链的长度为\(d\)的最大收益,那么转移时只要考虑一个点的子节点如何进行合并,注意到只有\(1,3\)消,\(2,2\)消两种互消的\(\text{case}\),相当于转移相当于\(\text{fix}\)\(a-c=d_{1}(|d_{1}|\leqslant1)\)且\(b\)\(\te......
  • [PA2021] Od deski do deski
    [PA2021]Oddeskidodeski看似简单,实则考察的是选手的DP基本功,如果像我一样只会观察性质就做不出来这题。性质:合法的序列一定是由若干个子串按照顺序拼起来的,其中每个子串的开头和结尾是一样的。然后的想法就是设\(f_i\)表示子串\(i\)能一次消掉的方案数,然后就会一直算......
  • P8386 [PA2021] Od deski do deski
    P8386[PA2021]Oddeskidodeski洛谷:OddeskidodeskiLOJ3600OddeskidodeskiSolution先考虑如何判定一个序列\(a\)是否合法。记\(dp_{i}=0/1\)表示考虑前\(i\)个数是否能被删空:\(dp_{i}\xleftarrow{\texttt{or}}dp_{j-1}[a_i=a_j]\)。初始化\(dp_{0}=......
  • [PA2021] Od deski do deski (dp)
    计数数列个数,要满足能划分为若干个两端相等区间。首先容易想到DP。我想的是按段分阶段转移,显然不行,因为很容易算重,一个数列能有多种划分方案则会被算多次。因此直接计数数列的每位,\(g(i,j)\)表示前\(i\)位有\(j\)种值存在位置的前一位往前的数列为合法序列的合法序列方案数,\(f(......
  • 题解:[PA2021] Drzewo czerwono-czarne
    题目链接:[PA2021]Drzewoczerwono-czarne首先对于起始和终止相同以及起始中只有一种颜色并且终止和起始不相同这两种情况是平凡的。考虑最后一步,一定是将某一条边上的一......