题解:
第一题:很明显是一个dp,但自己D不出来啊,发现前面会影响后面,后面会影响前面,不满足性质啊,当我们从前或从后都推不起走时,就可以想到补集转换了(又是它O__O"…)
t[i] 表示前i个车站所有方案,f[i]表示从一连到i的合法方案,p[i]表示连到前i个的不合法方案;
显然不合法的很好求,就是f[j]*i^(i-j) ,因为前面的只能连到j,剩下的(i-j)条随便选;f[i] = tot[i] - p[i];
#include#define ll long longusing namespace std;const ll mod = 1e9 +7;const int M = 5005;ll p[M], f[M], t[M];ll power(ll a, ll b){ ll ret = 1; for(; b; b >>= 1, a = (a * a) % mod) if(b & 1) ret = (ret * a) % mod; return ret;}void init(int n){ for(int i = 2; i <= n; i++) t[i] = power(i, i);};int main(){ freopen("rail.in","r",stdin); freopen("rail.out","w",stdout); int n; scanf("%d", &n); init(n); p[1] = 0; f[1] = t[1] = 1; for(int i = 2; i <= n; i++){ ll pp = 1; for(int j = i-1; j >= 0; j--) pp = pp * i % mod, p[i] = (p[i] + f[j] * pp) % mod; f[i] = ( (t[i] - p[i]) % mod + mod) % mod; } printf("%I64d\n", f[n]); return 0;}
第二题:显然可以忽略增加的亮度只能为非负这个条件,只操作第二个,设将第二个旋转了? 位,每个亮度增加了?,则答案为
这还要FFT常用的一个技巧; 就是把其中一个数组下标取反, 使 i = (n - i - 1), 那么就满足卷积的情况, 最后得到的C[i]就是x指数为i的系数;
#include#define ll long longusing namespace std;const ll mod = 998244353;const int M = 1000005;int l, r, rec[M];const double pi = acos(-1);struct vec{ double x, y; vec operator + (const vec &z) const { return (vec){x+z.x, y+z.y}; } vec operator - (const vec &z) const { return (vec){x-z.x, y-z.y}; } vec operator * (const vec &z) const { return (vec){x*z.x - y*z.y, x*z.y + y*z.x}; }}a[M], b[M], w[M];void fft(vec f[]){ int nr = r, k, j; vec x, u, v; for(int i = 1; i < l; i++) if(i < rec[i]) swap(f[i], f[rec[i]]); for(k = 1, nr--; k < l; k <<= 1, nr--) for(int i = 0; i < l; i += k << 1) for(j = 0, x = (vec){ 1, 0}; j < k; j++, x = x * w[nr]) u = f[i + j], v = x * f[i + j + k], f[i + j] = u + v, f[i + j + k] = u - v;}int main(){ freopen("gift.in","r",stdin); freopen("gift.out","w",stdout); int n, m, pfa = 0, pfb = 0, zj = 0; double z = -1e8; scanf("%d%d", &n, &m); for(int i = 0; i < n; i++){ double c; scanf("%lf", &c); a[i].x = c; pfa += c * c; zj += c; } for(int i = n-1; i >= 0; i--){ double c; scanf("%lf", &c); b[i].x = c; pfb += c * c; zj -= c; } for(l = 1, r = 0; l < (n<<1); l <<= 1, r++); for(int i = 1; i <= l; i++) rec[i] = (rec[i>>1] >> 1) | ((i&1) << (r-1)); w[0] = (vec){cos(2 * pi / l), sin(2 * pi / l)}; for(int i = 1; i <= r; i++) w[i] = w[i - 1] * w[i - 1]; fft(a); fft(b); for(int i = 0; i < l; i++) a[i] = a[i] * b[i]; w[0].y = -w[0].y; for(int i = 1; i <= r; i++) w[i] = w[i - 1] * w[i - 1]; fft(a); for(int i = 0; i < n - 1; i++)a[i + n].x += a[i].x; for(int i = n-1; i < 2*n - 1; i++)z = max(z, a[i].x); int dcz = int(floor( -zj * 1.0 / n + 0.5)); int ans = pfa + pfb + dcz*dcz*n + 2*zj*dcz - 2*int(z / l + 0.5); printf("%d\n",ans);}
第三题:
只有std粘了,自己没写出来
#include#define V_MAX 400#define MOD 998244353typedef void vac;typedef bool bnt;typedef long long lnt;inline lnt mod(lnt x) { return x < MOD ? x : x % MOD; }inline lnt pow(lnt a, int k){ if (a < 0) return 0; register lnt w; for (w = 1; k; a = mod(a * a), k >>= 1) if (k & 1) w = mod(w * a); return w;}int p[V_MAX + 1], n[V_MAX + 1];inline vac ini(int V){ for (register int u = 1; u <= V; ++u) p[u] = u;}inline int fnd(int u){ return p[u] != u ? p[u] = fnd(p[u]) : u;}inline vac uni(int u, int v){ p[fnd(u)] = fnd(v);}int V, w, i, j, u, v, x, D[V_MAX + 1][V_MAX + 1];bnt E[V_MAX + 1][V_MAX + 1];lnt C[V_MAX + 1][V_MAX + 1], f[V_MAX + 1], ans;int main(){ freopen("roof.in", "r", stdin); freopen("roof.out", "w", stdout); scanf("%d %d", &V, &w); ini(V); for (u = 1; u <= V; ++u) for (v = 1; v <= V; ++v) { scanf("%d", &D[u][v]); if (D[u][v] == 0) uni(u, v);//把边权为0的放在一个并查集里 } for (u = 1; u <= V; ++u) for (v = u + 1; v <= V; ++v) if (D[u][v] != D[v][u]) return puts("0") == EOF; for (u = 1; u <= V; ++u) for (v = u; v <= V; ++v) if (fnd(u) == fnd(v) && D[u][v]) return puts("0") == EOF; for (u = 1; u <= V; ++u) if (u == fnd(u)) for (v = u + 1; v <= V; ++v) if (v == fnd(v)) { for (x = 1; x <= V && (fnd(x) == u || fnd(x) == v || D[u][v] != D[u][x] + D[x][v]); ++x); E[u][v] = (x <= V);//存在dis[u][k] + dis[k][v] == dis[u][v] } for (i = 0; i <= V; ++i) { C[i][0] = 1; for (j = 1; j <= i; ++j) C[i][j] = mod(C[i - 1][j - 1] + C[i - 1][j]); } for (i = 1; i <= V; ++i)//图的连通性 { f[i] = pow(w + 1, int(C[i][2])); for (j = 1; j < i; ++j) f[i] = mod(f[i] - mod(mod(f[j] * C[i - 1][j - 1]) * mod(pow(w + 1, int(C[i - j][2])) * pow(w, j * (i - j)))) + MOD); } for (u = 1; u <= V; ++u)//并查集大小 ++n[fnd(u)]; ans = 1; for (u = 1; u <= V; ++u) if (fnd(u) == u) ans = mod(ans * f[n[u]]); for (u = 1; u <= V; ++u) if (fnd(u) == u) for (v = u + 1; v <= V; ++v) if (fnd(v) == v)//若有可放缩的边,就可以选择 w - D[u][v] + 1 条边使两个联通块连, 不然减去可供自由选择的方案 ans = mod(ans * mod(pow(w - D[u][v] + 1, n[u] * n[v]) - (E[u][v] ? 0 : pow(w - D[u][v], n[u] * n[v])) + MOD)); printf("%d\n", int(ans)); return 0;}