博客

  • CF1150D Three Religions

    完了我已经掉成Pupil了..

    考虑对于每次询问做一个DP处理。

    设$f(i,j,k)$表示使得串1的前i位,串2的前j位,串2的前k位能在主串中同时不相交出现的最短的前缀长度。

    设$g(n,ch)$表示第n个字符之后,ch第一次出现的位置,如果不存在则设为$\infty$。

    转移时考虑$f(i,j,k)=min(g([i\geq 1]f(i-1,j,k),S_1(i)),g([j\geq 1]f(i,j-1,k),S_2(j)),g([k\geq 1]f(i,j,k-1),S_3(k)))$。

    边界条件$f(0,0,0)=0$。

    可是这样单组询问复杂度是$O(250^3)$的,尽管这也是个常数。

    但是注意到每组询问要么是追加一个字符,要么是删除末尾的字符。

    假设在第i个串后追加一个字符,那么只需要把第i个串的DP数组推进一维即可。

    删除第i个串末尾的字符,只需要把DP数组其他两维置INF。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <inttypes.h>
    #include <cstring>
    #include <assert.h>
    #define debug(x) std::cout << #x << " = " << x << std::endl;
    
    typedef long long int int_t;
    
    using std::cin;
    using std::endl;
    using std::cout;
    const int_t INF = 0x7fffffff;
    const int_t LARGE = 250;
    //µÚn¸ö×Ö·ûºó£¬×Ö·ûcµÚÒ»´Î³öÏÖµÄλÖÃ
    int first[100001][26];
    int dp[251][251][251];
    int pos[4];
    int strs[4][252];
    char str[100010];
    int n,m;
    int at(int pos,char chr) {
    #ifdef DEBUG
    	cout<<"at "<<pos<<" "<<chr<<" = ";
    #endif
    	if(pos>n) {
    #ifdef DEBUG
    		cout<<0x3f3f3f3f<<endl;
    #endif
    		return 0x3f3f3f3f;
    	}
    #ifdef DEBUG
    	cout<<first[pos][chr-'a']<<endl;
    #endif
    	return first[pos][chr-'a'];
    }
    int main() {
    //    cin >> n >> m;
    	scanf("%d%d%s",&n,&m,str + 1);
    	memset(first[n],0x3f,sizeof(first[n]));
    //	cout<<first[n][1]<<endl;
    	for(int_t i = n; i >= 1; i --) {
    		memcpy(first[i-1],first[i],sizeof(first[i]));
    		first[i-1][str[i]-'a']=i;
    
    	}
    #ifdef DEBUG
    	for(int i=0; i<n; i++) for(char chr='a'; chr<='d'; chr++) cout<<"first "<<i<<" "<<chr<<" = "<<first[i][chr-'a']<<endl;
    
    #endif
    	memset(dp,0x3f,sizeof(dp));
    	dp[0][0][0]=0;
    	for(int i=1; i<=m; i++) {
    		static char opt[3];
    		int id;
    		scanf("%s%d",opt,&id);
    		if(opt[0]=='+') {
    			char chr,strx[3];
    			scanf("%s",&strx);
    			chr=strx[0];
    			strs[id][++strs[id][0]]=chr;
    			int len=strs[id][0];
    			if(id==1) {
    //				for(int i=0; i<=strs[1][0]; i++)
    				{
    					int i=strs[1][0];
    					for(int j=0; j<=strs[2][0]; j++) {
    						for(int k=0; k<=strs[3][0]; k++) {
    							if(i-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i-1][j][k],strs[1][i]));
    							if(j-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j-1][k],strs[2][j]));
    							if(k-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j][k-1],strs[3][k]));
    #ifdef DEBUG
    							cout<<"dp "<<i<< " "<<j<<" "<<k<<" = "<<dp[i][j][k]<<endl;
    #endif
    						}
    					}
    				}
    			} else if(id==2) {
    				for(int i=0; i<=strs[1][0]; i++) {
    //				for(int j=0; j<=strs[2][0]; j++)
    					int j=strs[2][0];
    					{
    						for(int k=0; k<=strs[3][0]; k++) {
    							if(i-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i-1][j][k],strs[1][i]));
    							if(j-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j-1][k],strs[2][j]));
    							if(k-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j][k-1],strs[3][k]));
    #ifdef DEBUG
    							cout<<"dp "<<i<< " "<<j<<" "<<k<<" = "<<dp[i][j][k]<<endl;
    #endif
    						}
    					}
    				}
    			} else {
    				for(int i=0; i<=strs[1][0]; i++) {
    					for(int j=0; j<=strs[2][0]; j++) {
    						int k=strs[3][0];
    //					for(int k=0; k<=strs[3][0]; k++)
    						{
    							if(i-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i-1][j][k],strs[1][i]));
    							if(j-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j-1][k],strs[2][j]));
    							if(k-1>=0) dp[i][j][k]=std::min(dp[i][j][k], at(dp[i][j][k-1],strs[3][k]));
    #ifdef DEBUG
    							cout<<"dp "<<i<< " "<<j<<" "<<k<<" = "<<dp[i][j][k]<<endl;
    #endif
    						}
    					}
    				}
    			}
    		} else {
    
    			if(id==1) {
    				for(int j=0; j<=strs[2][0]; j++)
    					for(int k=0; k<=strs[3][0]; k++) {
    						dp[strs[id][0]][j][k]=INF;
    					}
    			} else if(id==2) {
    				for(int i=0; i<=strs[1][0]; i++)
    //                for(int j=1;j<=strs[2][0];j++)
    					for(int k=0; k<=strs[3][0]; k++) {
    						dp[i][strs[2][0]][k]=INF;
    					}
    
    			} else {
    				for(int i=0; i<=strs[1][0]; i++)
    					for(int j=0; j<=strs[2][0]; j++) {
    //			        for(int k=1;k<=strs[3][0];k++){
    						dp[i][j][strs[3][0]]=INF;
    					}
    
    			}
    			strs[id][0]--;
    		}
    		if(dp[strs[1][0]][strs[2][0]][strs[3][0]]<=n) {
    			printf("yes\n");
    		} else {
    			printf("no\n");
    		}
    	}
    	return 0;
    }

     

  • BJOI2019 奥术神杖

    大量的细节+自己不熟悉AC自动机。

    1. 基于DFS的记忆化搜索是错的,转移顺序有问题。
    2. 走到了一个点,即代表这个点在失配树上到根的所有点都匹配到了,所以要把他们加起来。
    3. DP各种东西好好想想,别眼瞎。
    4. 分清楚这步转移时从哪到哪的

    原题的式子取个log,变成了经典的取若干个东西使得他们的平均值最大的问题,01分数规划即可。

    给每个串加上一个附加权,然后AC自动机跑DP,最大化权值即可。

    #include <assert.h>
    #include <cmath>
    #include <cstring>
    #include <iomanip>
    #include <iostream>
    #include <queue>
    #include <string>
    #include <unordered_map>
    #include <utility>
    using int_t = long long int;
    using real_t = double;
    using pair_t = std::pair<int_t, int_t>;
    const real_t EPS = 1e-7;
    const int_t INF = 0x7fffffff;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 2000;
    struct Node* nodes[LARGE + 1];
    int_t used = 0;
    struct Node {
        real_t val = 0;
        Node* link = nullptr;
        Node* chds[10];
        int_t count = 0;
        int_t id = 0;
        int_t idx = 0;
    
        Node*& access(char chr) { return chds[chr - '0']; }
        Node() {
            idx = ++used;
            nodes[idx] = this;
            memset(chds, 0, sizeof(chds));
        }
    };
    Node* insert(Node* root, const char* str, real_t val) {
        for (auto p = str; *p != '\0'; p++) {
            if (root->access(*p) == nullptr) {
                root->access(*p) = new Node;
            }
            root = root->access(*p);
        }
        root->val += val;
        root->count += 1;
        return root;
    }
    void BFS(Node* root) {
        std::queue<Node*> queue;
        for (Node*& to : root->chds)
            if (to) {
                to->link = root;
                queue.push(to);
            } else {
                to = root;
            }
        while (queue.empty() == false) {
            Node* front = queue.front();
            queue.pop();
            front->count += front->link->count;
            front->val += front->link->val;
            for (auto chr = '0'; chr <= '9'; chr++) {
                Node*& to = front->access(chr);
                if (to == nullptr) {
                    to = front->link->access(chr);
                } else {
                    Node* parent = front->link;
                    while (parent && parent->access(chr) == nullptr)
                        parent = parent->link;
                    if (parent == nullptr)
                        to->link = root;
                    else
                        to->link = parent->access(chr);
                    queue.push(to);
                }
            }
        }
    }
    
    char buf[LARGE + 1];
    int_t n, m;
    
    Node* root = new Node;
    //已匹配长度为n,在第m个点的最优解
    real_t dp[LARGE + 1][LARGE + 1];
    pair_t from[LARGE + 1][LARGE + 1];
    char string[LARGE + 1];
    real_t check(real_t x) {
    
        for (int_t i = 0; i <= n; i++) {
            for (int_t j = 1; j <= used; j++) {
                dp[i][j] = -INF;
            }
        }
        dp[0][1] = 0;
        for (int_t i = 0; i < n; i++) {
            for (int_t j = 1; j <= used; j++) {
                if (dp[i][j] == -INF) continue;
                //枚举出边
                if (buf[i] != '.') {
                    auto chr = buf[i];
                    Node* node = nodes[j]->access(chr);
                    if (dp[i + 1][node->idx] <
                        dp[i][j] + node->val - x * node->count) {
                        dp[i + 1][node->idx] =
                            dp[i][j] + node->val - x * node->count;
                        from[i + 1][node->idx] = pair_t(j, chr);
                    }
    
                } else {
                    for (auto chr = '0'; chr <= '9'; chr++) {
                        Node* node = nodes[j]->access(chr);
                        if (dp[i + 1][node->idx] <
                            dp[i][j] + node->val - x * node->count) {
                            dp[i + 1][node->idx] =
                                dp[i][j] + node->val - x * node->count;
                            from[i + 1][node->idx] = pair_t(j, chr);
                        }
                    }
                }
            }
        }
        int_t max = -1, val = -INF;
        for (int_t i = 1; i <= used; i++)
            if (dp[n][i] > val) {
                val = dp[n][i];
                max = i;
            }
        int_t pos = max;
        for (int_t i = n; i >= 1; i--) {
            string[i - 1] = from[i][pos].second;
            pos = from[i][pos].first;
        }
        return dp[n][max];
    }
    int main() {
        cout.setf(std::ios::fixed);
        cout << std::setprecision(5);
        scanf("%lld%lld%s", &n, &m, buf);
        for (int_t i = 1; i <= m; i++) {
            static char buf[LARGE + 1];
            int_t val;
            scanf("%s%lld", buf, &val);
            insert(root, buf, log(val));
        }
        BFS(root);
        real_t left = 0, right = 25;
        real_t mid;
        real_t result;
        while ((right - left) > EPS) {
            mid = (left + right) / 2;
            real_t checkval = check(mid);
            if (checkval > 0) {
                result = left = mid;
            } else {
                right = mid;
            }
        }
        check(result);
        printf("%s", string);
        return 0;
    }

     

  • BJOI2019 勘破神机

    强行把两道不相关的题合到一起?不过还好,毕竟可以出到任意二阶递推数列(三阶的其实也可以,只要有人会解三次方程

    $$ \text{考虑第一部分:} \\ \sum_{1\le i\le n}{G\left( i,k \right)}=\sum_{1\le i\le n}{\left( \begin{array}{c} F_i\\ k\\ \end{array} \right) =\frac{1}{k!}\sum_{1\le i\le n}{F_i^{\underline{k}}}},\text{其中}F_i\text{为斐波那契数列,}F_0=\text{1,}F_1=1 \\ =\frac{1}{k!}\sum_{1\le i\le n}{\prod_{0\le j<k}{\left( F_i-i \right)}} \\ \text{构造}k\text{次多项式}H\left( x \right) =\prod_{0\le j<k}{\left( x-j \right)}=\sum_{0\le i\le k}{h_ix^i} \\ \text{则有}\sum_{1\le i\le n}{G\left( i,k \right)}=\frac{1}{k!}\sum_{1\le i\le n}{\sum_{0\le j\le k}{h_jF_{i}^{j}}} \\ =\frac{1}{k!}\sum_{0\le j\le k}{h_j\sum_{1\le i\le n}{F_{i}^{j}}} \\ \text{设}f\left( n,k \right) =\sum_{1\le i\le n}{F_{i}^{k}} \\ \text{由}F_i=\frac{5+\sqrt{5}}{10}\left( \frac{1+\sqrt{5}}{2} \right) ^n+\frac{5-\sqrt{5}}{10}\left( \frac{1-\sqrt{5}}{2} \right) ^n,\text{设}a=\frac{5+\sqrt{5}}{10},b=\frac{5-\sqrt{5}}{10} \\ x_1=\frac{1+\sqrt{5}}{2},x_2=\frac{1-\sqrt{5}}{2} \\ \text{则}F_i=ax_{1}^{n}+bx_{2}^{n} \\ \text{构造模意义下的数域}a+b\sqrt{5} \\ \text{则有}f\left( n,k \right) =\sum_{1\le i\le n}{F_{i}^{k}}=\sum_{1\le i\le n}{\left( ax_{1}^{i}+bx_{2}^{i} \right) ^k}=\sum_{1\le i\le n}{\sum_{0\le j\le k}{\left( \begin{array}{c} k\\ j\\ \end{array} \right) a^jx_{1}^{ij}b^{k-j}x_{2}^{i\left( k-j \right)}}} \\ =\sum_{0\le j\le k}{\left( \begin{array}{c} k\\ j\\ \end{array} \right) a^jb^{k-j}\sum_{1\le i\le n}{x_{1}^{ij}x_{2}^{i\left( k-j \right)}}} \\ =\sum_{0\le j\le k}{\left( \begin{array}{c} k\\ j\\ \end{array} \right) a^jb^{k-j}\sum_{1\le i\le n}{\left( x_{1}^{j}x_{2}^{k-j} \right) ^i}}=\sum_{0\le j\le k}{\left( \begin{array}{c} k\\ j\\ \end{array} \right) a^jb^{k-j}\frac{\left( x_{1}^{j}x_{2}^{k-j} \right) ^{n+1}-1}{x_{1}^{j}x_{2}^{k-j}-1}} \\ \text{第二个求和号等比数列求和即可。} \\ \text{第一部分复杂度}O\left( k^2\log k \right) \\ \text{考虑第二部分。} \\ \text{设}f\left( n \right) \text{表示长度为}n\text{的答案,打表可知} \\ f\left( 2n \right) =4f\left( 2n-2 \right) -f\left( 2n-4 \right) \\ \text{只考虑取偶数的}2n,\text{则}f\left( n \right) =4f\left( n-1 \right) -f\left( n-2 \right) \text{。} \\ \text{计算特征根}x_1=2+\sqrt{3},x_2=2-\sqrt{3} \\ \text{设}f\left( n \right) =ax_{1}^{n}+bx_{2}^{n},\text{由}f\left( 0 \right) =\text{1,}f\left( 1 \right) =\text{3得} \\ a=\frac{3+\sqrt{3}}{6},b=\frac{3-\sqrt{3}}{6} \\ \text{仍然像第一部分一样处理即可。} \\ $$

    #include <assert.h>
    #include <algorithm>
    #include <cstring>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t mod = 998244353;
    const int_t LARGE = 1000;
    
    template <class T>
    T power(T base, int_t index) {
        base = (base % mod + mod) % mod;
        T result = 1;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    template <int_t I>
    struct Z {
        int_t a = 0, b = 0;
        Z(int_t a = 0, int_t b = 0) : a(a), b(b) {}
        Z<I> operator+(const Z<I>& rhs) const {
            return Z<I>{(a + rhs.a) % mod, (b + rhs.b) % mod};
        }
        Z<I> operator-(const Z<I>& rhs) const {
            return Z<I>{(a - rhs.a + mod) % mod, (b - rhs.b + mod) % mod};
        }
        Z<I> operator*(const Z<I>& rhs) const {
            return Z<I>{(a * (rhs.a % mod) % mod + I * b % mod * rhs.b % mod) % mod,
                        (a * rhs.b % mod + b * rhs.a % mod) % mod};
        }
        Z<I> inv() const {
            int_t under = power(
                (a * a % mod - I * b % mod * b % mod + I * mod) % mod, mod - 2);
            return Z<I>{a * under % mod, (mod - b) * under % mod};
        }
        Z<I> operator/(const Z<I>& rhs) const { return (*this) * rhs.inv(); }
        Z<I> operator%(int_t x) const { return Z<I>{a % x, b % x}; }
    };
    int_t fact[LARGE + 1], inv[LARGE + 1], factInv[LARGE + 1];
    int_t C(int_t n, int_t m) {
        return fact[n] * factInv[m] % mod * factInv[n - m] % mod;
    }
    template <int_t I>
    Z<I> sumQ(Z<I> x, int_t n) {
        if (x.a == 1 && x.b == 0) return n;
        return (power(x, n) - 1) / (x - 1);
    }
    //求斐波那契数列的k次幂的前缀和
    int_t sumF(int_t n, int_t k) {
        if (k == 0) return n + 1;
        using Z = ::Z<5>;
        const Z a = Z(5, 1) / 10;
        const Z b = Z(5, mod - 1) / 10;
        const Z x1 = Z(1, 1) / 2, x2 = Z(1, mod - 1) / 2;
        Z result = 0;
        for (int_t i = 0; i <= k; i++) {
            Z x = power(x1, i) * power(x2, k - i);
            result =
                result + power(a, i) * C(k, i) * power(b, k - i) * sumQ(x, n + 1);
        }
        assert(result.b == 0);
        return result.a;
    }
    int_t sumG(int_t n, int_t k) {
        if (k == 0) return n + 1;
        using Z = ::Z<3>;
        const Z a = Z(3, 1) / 6;
        const Z b = Z(3, mod - 1) / 6;
        const Z x1 = Z(2, 1), x2 = Z(2, mod - 1);
        Z result = 0;
        for (int_t i = 0; i <= k; i++) {
            Z x = power(x1, i) * power(x2, k - i);
            result =
                result + power(a, i) * C(k, i) * power(b, k - i) * sumQ(x, n + 1);
        }
        assert(result.b == 0);
        return result.a;
    }
    void poly_mul(const int_t* A, int_t n, const int_t* B, int_t m, int_t* C) {
        static int_t Cx[LARGE * 2 + 1];
        std::fill(Cx, Cx + n + m + 1, 0);
        for (int_t i = 0; i <= n; i++)
            for (int_t j = 0; j <= m; j++) {
                Cx[i + j] = (Cx[i + j] + A[i] * B[j] % mod) % mod;
            }
        std::copy(Cx, Cx + n + m + 1, C);
    }
    int_t poly[LARGE + 1];
    template <class Func>
    int_t process(int_t n, int_t k, Func F) {
        int_t result = 0;
        for (int_t i = 0; i <= k; i++) {
            result = (result + poly[i] * F(n, i) % mod) % mod;
        }
        result = result * factInv[k] % mod;
        return result;
    }
    int main() {
        fact[0] = fact[1] = factInv[0] = factInv[1] = inv[1] = 1;
        for (int_t i = 2; i <= LARGE; i++) {
            inv[i] = (mod - mod / i) * inv[mod % i] % mod;
            fact[i] = fact[i - 1] * i % mod;
            factInv[i] = factInv[i - 1] * inv[i] % mod;
        }
        int_t T, m, left, right, k;
        cin >> T >> m >> left >> right >> k;
        poly[0] = 1;
        for (int_t i = 0; i < k; i++) {
            int_t curr[] = {(mod - i) % mod, 1};
            poly_mul(poly, k, curr, 1, poly);
        }
        if (m == 2) {
            int_t result =
                (process(right, k, sumF) - process(left - 1, k, sumF) + mod) % mod *
                power(right - left + 1, mod - 2) % mod;
            cout << result << endl;
        } else {
            int_t result = (process(right / 2, k, sumG) -
                            process((left - 1) / 2, k, sumG) + mod) %
                           mod * power(right - left + 1, mod - 2) % mod;
            cout << result << endl;
        }
        return 0;
    }

     

  • BJOI2019 排兵布阵

    送分题*2

    普及分组背包板子。

    这题出省选真的好吗??

    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 300;
    int_t mat[LARGE + 1][LARGE + 1];
    int_t s, n, m;
    
    struct Item {
        int_t cost, val;
    };
    
    std::vector<Item> items[LARGE + 1];
    int_t dp[301][20001];
    int main() {
        cin >> s >> n >> m;
        for (int_t i = 1; i <= s; i++) {
            for (int_t j = 1; j <= n; j++) cin >> mat[i][j];
        }
        //枚举每一列
        for (int_t i = 1; i <= n; i++) {
            std::vector<int_t> numbers;
            for (int_t j = 1; j <= s; j++) numbers.push_back(mat[j][i]);
            std::sort(numbers.begin(), numbers.end());
            for (int_t j = 0; j < numbers.size(); j++) {
                if (numbers[j] * 2 + 1 <= m)
                    items[i].push_back(Item{numbers[j] * 2 + 1, (j + 1) * i});
    #ifdef DEBUG
                cout << "item cost = " << numbers[j] * 2 + 1
                     << " val = " << (j + 1) * i << " for col " << i << endl;
    #endif
            }
        }
        for (int_t i = 1; i <= n; i++) {
            memcpy(dp[i], dp[i - 1], sizeof(dp[i]));
            for (const Item& item : items[i]) {
                for (int_t j = item.cost; j <= m; j++) {
                    dp[i][j] =
                        std::max(dp[i - 1][j - item.cost] + item.val, dp[i][j]);
                }
            }
        }
        int_t result = 0;
        for (int_t i = 0; i <= m; i++) result = std::max(result, dp[n][i]);
        cout << result << endl;
        return 0;
    }

     

  • BJOI2019 光线

    怎么BJOI这么多送分题啊

    $$ \text{设}f\left( n \right) \text{表示把前}n\text{块玻璃视为一个整体,从第一块玻璃入射的光穿透前}n\text{块玻璃的透光率。} \\ \text{设}g\left( n \right) \text{表示把前}n\text{块玻璃视为一个整体,从第}n\text{块玻璃入射,仍然能从第}n\text{块玻璃射出来的反射率。} \\ \text{显然有}f\left( 1 \right) =a_1,g\left( 1 \right) =b_1 \\ \text{对于}n>\text{1有}f\left( n \right) =\sum_{i\ge 0}{f\left( n-1 \right) \left( g\left( n-1 \right) b_n \right) ^i}a_n\left( \text{枚举光线在}n\text{和}n-\text{1之间的反射次数} \right) \\ =f\left( n-1 \right) a_n\sum_{i\ge 0}{\left( g\left( n-1 \right) b_n \right) ^i}=\frac{f\left( n-1 \right) a_n}{1-g\left( n-1 \right) b_n} \\ g\left( n \right) =b_n+\sum_{i\ge 0}{a_ng\left( n-1 \right) \left( g\left( n-1 \right) b_n \right) ^ia_n}=b_n+\frac{g\left( n-1 \right) a_{n}^{2}}{1-b_ng\left( n-1 \right)} \\ \left( \text{先考虑直接在第}n\text{块反射了的情况,然后枚举在}n\text{和}n-\text{1之间的反射次数} \right) $$

    #include <iostream>
    
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 1e6;
    
    const int_t mod = 1e9 + 7;
    constexpr int_t power(int_t base, int_t index) {
        int_t result = 1;
        index = (index % (mod - 1) + mod - 1) % (mod - 1);
        base = (base % mod + mod) % mod;
        while (index) {
            if (index & 1) result = result * base % mod;
            index >>= 1;
            base = base * base % mod;
        }
        return result;
    }
    const int_t inv100 = power(100, -1);
    int main() {
        static int_t f[LARGE + 1], g[LARGE + 1], a[LARGE + 1], b[LARGE + 1];
        int_t n;
        cin >> n;
        for (int_t i = 1; i <= n; i++) {
            int_t x, y;
            scanf("%lld%lld", &x, &y);
            a[i] = x * inv100 % mod;
            b[i] = y * inv100 % mod;
        }
        f[1] = a[1], g[1] = b[1];
        for (int_t i = 2; i <= n; i++) {
            f[i] = (f[i - 1] * a[i] % mod) * power(1 - g[i - 1] * b[i], -1) % mod;
            g[i] = (b[i] + (a[i] * a[i] % mod * g[i - 1] % mod *
                            power(1 - b[i] * g[i - 1] % mod, -1))) %
                   mod;
        }
        cout << f[n] << endl;
        return 0;
    }

     

  • CometOJ 37F 真实无妄她们的人生之路

    第一道非板子的多点求值题。

    题意:

    有$n$个物品,每个物品使用后有$p_i$的概率升一级(初始时等级为0),有$1-p_i$的概率不升级。

    等级为$i$时的攻击力为$a_i$。

    现在要求对于每一个$i\in [1,n]$,依次求出不使用第$i$件物品,但是使用了剩下的$n-1$件物品时,攻击力的期望。

    $n\leq 10^5$,所有运算在模$998244353$意义下进行。

    时限$10s$(Comet OJ的评测机非常烂)。

    题解

    对于第$i$个物品,构造多项式$C_i(x)=1-p_i+p_ix$,显然不使用第$i$个物品,但是使用了剩下所有物品的概率生成函数为$P_i(x)=\frac {\prod_{1\leq j\leq n}C_j(x)}{C_j(x)}$,计算这个多项式对序列${a_i}$的点积即不使用第$i$个物品的答案。

    但是很显然这样只能做到$O(n^2+n\log n)$,考虑其他做法。

    $$ \text{构造一个多项式函数}g\left( F \right) =\sum_{0\le i\le n-1}{a_i\times \left[ x^i \right] F\left( x \right)} \\ \text{显然则有}g\left( F+G \right) =g\left( F \right) +g\left( G \right) ,\text{其中}F,G\text{均为多项式。} \\ g\left( cF \right) =cg\left( F \right) ,\text{其中}c\text{是常数。} \\ \text{令}F\left( x \right) =\prod_{1\le i\le n}{\left( 1-p_i+p_ix \right)}, \\ \text{我们要求的结果则为所有的}g\left( \frac{F\left( x \right)}{1-p_i+p_ix} \right) ,\text{即}\frac{1}{1-p_i}g\left( \frac{F\left( x \right)}{1+\frac{p_i}{1-p_i}x} \right) \text{。} \\ \text{如果}p_i=\text{1,那么显然可以比较便捷的计算出答案,以下假设}p_i\ne 1 \\ \text{由于}\frac{1}{1-x}=\sum_{i\ge 0}{x^i} \\ \text{故}\frac{1}{x}=\sum_{i\ge 0}{\left( 1-x \right) ^i} \\ \text{故}\frac{1}{1-p_i}g\left( \frac{F\left( x \right)}{1+\frac{p_i}{1-p_i}x} \right) =\frac{1}{1-p_i}g\left( \sum_{j\ge 0}{F\left( x \right) \left( -\frac{p_i}{1-p_i}x \right) ^j} \right) \\ \text{又由于}g\left( A+B \right) =g\left( A \right) +g\left( B \right) \\ \text{故}\frac{1}{1-p_i}\sum_{j\ge 0}{g\left( F\left( x \right) \left( -\frac{p_i}{1-p_i} \right) ^j\times x^j \right)} \\ \text{又由于}g\left( cA \right) =cg\left( A \right) \\ \text{可得}\frac{1}{1-p_i}\sum_{j\ge 0}{\left( -\frac{p_i}{1-p_i} \right) ^jg\left( F\left( x \right) x^j \right)} \\ \text{令多项式}G\left( y \right) =\sum_{j\ge 0}{y^jg\left( F\left( x \right) x^j \right)} \\ \text{则答案可以通过计算出多项式}G\left( y \right) \text{后通过多点求值}G\left( -\frac{p_1}{1-p_1} \right) ,G\left( -\frac{p_2}{1-p_2} \right) …G\left( -\frac{p_n}{1-p_n} \right) \text{得到。} \\ \text{显然在}j\ge n\text{时有}g\left( F\left( x \right) x^j \right) \text{为0,所以}G\left( y \right) \text{的次数为}n \\ \text{考虑}g\left( F\left( x \right) x^j \right) \text{如何计算,}g\left( F\left( x \right) x^j \right) =\sum_{0\le i\le n-1}{a_i\times \left[ x^i \right] F\left( x \right) x^j}=\sum_{0\le i\le n-1}{a_i\times \left[ x^{i-j} \right] F\left( x \right)} \\ \text{构造多项式}F_0\left( x \right) =\sum_{0\le i\le n-1}{a_ix^i},\text{然后计算}F_0\left( x \right) \left( xF^R\left( x \right) \right) ,\text{其}i+n\text{次项前的系数即为}g\left( F\left( x \right) x^i \right) \\ $$

    #pragma GCC optimize("O3")
    #include <assert.h>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <fstream>
    #include <iostream>
    #include <vector>
    // #include
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 5e5;
    const int_t mod = 998244353;
    const int_t g = 3;
    
    void transformX(int_t* A, int_t len, int_t g, int_t mod);
    void transformNTT(int_t* A, int_t size2, int_t arg);
    std::vector<int_t> poly_dc_mul(const std::vector<int_t>& A);
    void poly_inv(const int_t* A, int_t n, int_t* result);
    void poly_div(const int_t* A, int_t n, const int_t* B, int_t m, int_t* R);
    std::vector<int_t> poly_eval(const std::vector<int_t>& poly,
                                 const std::vector<int_t>& vals);
    
    void poly_mul(const int_t* A, int_t n, const int_t* B, int_t m, int_t* A0);
    int_t power(int_t base, int_t index) {
        int_t result = 1;
        base = (base % mod + mod) % mod;
        index = (index % (mod - 1) + mod - 1) % (mod - 1);
        while (index) {
            if (index & 1) result = (int_t)result * base % mod;
            index >>= 1;
            base = (int_t)base * base % mod;
        }
        assert(result >= 0);
        return result % mod;
    }
    
    int flips[20][LARGE];
    const auto flip = [=](int x, int size2) {
        int result = 0;
        for (int i = 1; i < size2; i++) {
            result |= (x & 1);
            x >>= 1;
            result <<= 1;
        }
        return result | (x & 1);
    };
    std::vector<int_t> poly_dc_mul2(const std::vector<int_t>& A) {
        if (A.size() == 1) {
            return std::vector<int_t>{(int_t)((1 - A[0] + mod) % mod), A[0]};
        }
        std::vector<int_t> left, right;
        for (int i = 0; i < A.size(); i++) {
            if (i < A.size() / 2)
                left.push_back(A[i]);
            else
                right.push_back(A[i]);
        }
        auto leftr = poly_dc_mul2(std::move(left)),
             rightr = poly_dc_mul2(std::move(right));
        std::vector<int_t> result(leftr.size() + rightr.size() + 5);
        poly_mul(&leftr.front(), leftr.size() - 1, &rightr.front(),
                 rightr.size() - 1, &result.front());
        while (result.empty() == false && result.back() == 0) result.pop_back();
        return result;
    }
    
    int main() {
        for (int i = 1; i < 19; i++) {
            for (int j = 1; j < (1 << i); j++) flips[i][j] = flip(j, i);
        }
        int_t n;
        static int_t pros[LARGE + 1], as[LARGE + 1], Fx[LARGE + 1];
        std::vector<int_t> F;
        scanf("%lld", &n);
        for (int i = 0; i < n; i++) {
            scanf("%lld", &as[i]);
        }
        for (int i = 1; i <= n; i++) {
            int_t x, y;
            scanf("%lld%lld", &x, &y);
            pros[i] = ((int_t)x * ((int_t)power(y, -1) % mod)) % mod;
            assert(pros[i] < mod);
            F.push_back(pros[i]);
        }
        F = poly_dc_mul2(F);
        int_t oneans = 0;
        for (int i = 1; i <= n; i++) {
            oneans = (oneans + (int_t)F[i] * as[i - 1] % mod) % mod;
        }
        F.push_back(0);
        std::reverse(F.begin(), F.end());
        poly_mul(as, F.size() - 1, &F.front(), F.size() - 1, Fx);
        std::copy(Fx + F.size() - 1, Fx + F.size() - 1 + n, Fx);
        std::vector<int_t> poly(Fx, Fx + n);
        std::vector<int_t> point_ts;
        for (int i = 1; i <= n; i++) {
            point_ts.push_back(
                (mod - (int_t)pros[i] * (int_t)power(1 - pros[i] + mod, -1) % mod) %
                mod);
        }
        auto res = poly_eval(poly, point_ts);
        for (int i = 1; i <= n; i++) {
            if (pros[i] == 1) {
                printf("%lld ", oneans);
            } else {
                printf("%lld ",
                       (int_t)res[i - 1] * power(1 - pros[i] + mod, -1) % mod);
            }
        }
        return 0;
    }
    
    void poly_mul(const int_t* A, int_t n, const int_t* B, int_t m, int_t* A0) {
        static int_t Ax[LARGE + 1], Bx[LARGE + 1];
        int size2 = 0;
        while ((1 << size2) < n + m + 1) size2++;
        int len = 1 << size2;
        for (int i = 0; i < len; i++) {
            if (i <= n)
                Ax[i] = A[i];
            else
                Ax[i] = 0;
            if (i <= m)
                Bx[i] = B[i];
            else
                Bx[i] = 0;
        }
        transformNTT(Ax, size2, 1);
        transformNTT(Bx, size2, 1);
        for (int i = 0; i < len; i++) Ax[i] = (int_t)Ax[i] * Bx[i] % mod;
        transformNTT(Ax, size2, -1);
        const int_t inv = power(len, mod - 2);
        for (int i = 0; i <= n + m; i++) A0[i] = (int_t)Ax[i] * inv % mod;
    }
    void transformNTT(int_t* A, int_t size2, int_t arg) {
        for (int i = 0; i < (1 << size2); i++) {
            int x = flips[size2][i];
            if (x > i) std::swap(A[i], A[x]);
        }
        for (int i = 2; i <= (1 << size2); i *= 2) {
            int_t mr = power(g, (mod - 1) + (mod - 1) / i * arg);
            for (int j = 0; j < (1 << size2); j += i) {
                int_t curr = 1;
                for (int k = 0; k < i / 2; k++) {
                    int_t u = A[j + k] % mod,
                          t = (int_t)A[j + k + i / 2] * curr % mod;
                    A[j + k] = (u + t) % mod;
                    A[j + k + i / 2] = (int_t)((int_t)u - t + mod) % mod;
                    curr = (int_t)curr * mr % mod;
                }
            }
        }
    }
    void poly_inv(const int_t* A, int_t n, int_t* result) {
        if (n == 1) {
            result[0] = power(A[0], mod - 2);
            return;
        }
        poly_inv(A, n / 2 + n % 2, result);
        static int_t temp[LARGE + 1];
        std::fill(temp, temp + 2 * n, 0);
        int prev = n / 2 + n % 2;
        poly_mul(result, prev - 1, result, prev - 1, temp);
        poly_mul(A, n - 1, temp, n - 1, temp);
        for (int i = 0; i < n; i++)
            result[i] = (2 * result[i] % mod - temp[i] + 2 * mod) % mod;
    }
    //计算n次多项式A整除B次多项式m的余数
    void poly_div(const int_t* A, int_t n, const int_t* B, int_t m, int_t* R) {
        while (n >= 0 && A[n] == 0) n--;
        while (m >= 0 && B[m] == 0) m--;
        if (n < m) {
            std::copy(A, A + n + 1, R);
            return;
        }
        static int_t AR[LARGE + 1], BR[LARGE + 1];
        for (int i = 0; i <= n; i++) AR[i] = A[n - i];
        for (int i = 0; i <= m; i++) BR[i] = B[m - i];
        for (int i = m + 1; i <= n - m; i++) BR[i] = 0;
        static int_t inv[LARGE + 1], Q[LARGE + 1], C[LARGE + 1];
        std::fill(inv, inv + n - m + 1, 0);
        poly_inv(BR, n - m + 1, inv);
        poly_mul(AR, n - m, inv, n - m, Q);
        std::reverse(Q, Q + n - m + 1);
        poly_mul(Q, n - m, B, m, C);
        for (int_t i = 0; i < m; i++) R[i] = (A[i] - C[i] + mod) % mod;
    }
    std::vector<int_t> poly_dc_mul(const std::vector<int_t>& A) {
        if (A.size() == 1) {
            return std::vector<int_t>{(int_t)mod - A[0], 1};
        }
        std::vector<int_t> left, right;
        for (int i = 0; i < A.size(); i++) {
            if (i < A.size() / 2)
                left.push_back(A[i]);
            else
                right.push_back(A[i]);
        }
        auto leftr = poly_dc_mul(std::move(left)),
             rightr = poly_dc_mul(std::move(right));
        std::vector<int_t> result(leftr.size() + rightr.size() + 5);
        poly_mul(&leftr.front(), leftr.size() - 1, &rightr.front(),
                 rightr.size() - 1, &result.front());
        while (result.empty() == false && result.back() == 0) result.pop_back();
        return result;
    }
    
    // vals是求值点
    std::vector<int_t> poly_eval(const std::vector<int_t>& poly,
                                 const std::vector<int_t>& vals) {
        if (vals.size() <= 300) {
            std::vector<int_t> result;
            for (auto x : vals) {
                int_t curr = 0, pow = 1;
                for (auto y : poly) {
                    curr = ((int_t)curr + (int_t)y * pow % mod) % mod;
                    pow = (int_t)pow * x % mod;
                }
                result.push_back(curr);
            }
            return result;
        }
        std::vector<int_t> left, right;
        for (int i = 0; i < vals.size(); i++)
            if (i < vals.size() / 2)
                left.push_back(vals[i]);
            else
                right.push_back(vals[i]);
        auto leftres = poly_dc_mul(left);
        auto rightres = poly_dc_mul(right);
        std::vector<int_t> pleft(leftres.size() - 1), pright(rightres.size() - 1);
        poly_div(&poly.front(), poly.size() - 1, &leftres.front(),
                 leftres.size() - 1, &pleft.front());
        poly_div(&poly.front(), poly.size() - 1, &rightres.front(),
                 rightres.size() - 1, &pright.front());
        auto leftx = poly_eval(pleft, left);
        auto rightx = poly_eval(pright, right);
        // assert(leftres.size())
        for (auto x : rightx) leftx.push_back(x);
        return leftx;
    }

     

  • noi.ac 165染色

    一场由于忘记清空数组而引发的血案。

    下次我还是开对象处理多组数据吧

    首先判无解,如果存在任意一个点的度数小于2或者为奇数则无解。

    其他情况,让k成为最大的使得$2^k|gcd(degs)$的k,其中degs是全体点度数的集合。

    然后对原图的边进行交错染色,然后这就确定了第k-1位的取值。

    黑色的边的颜色的第k-1位设定为1,白色的边设定为0.

    然后把两种颜色的边拿出来,递归下去按照相同的方法确定第k-2位。

    # 不要忘记在判无解后清空数组!!!

    #include <assert.h>
    #include <algorithm>
    #include <iostream>
    #include <set>
    #include <unordered_set>
    #include <vector>
    using int_t = long long int;
    using set_t = std::unordered_set<int_t>;
    using std::cin;
    using std::cout;
    using std::endl;
    
    const int_t LARGE = 2e5;
    
    struct Edge {
        int_t to, id, from;
    } edges[LARGE + 1];
    
    std::vector<Edge> graph[LARGE + 1];
    int_t color[LARGE + 1];
    int_t degree[LARGE + 1];
    bool visited[LARGE + 1];
    int_t n, m;
    //从点vtx出发,这一位的掩码为mask,上一条边的颜色为lastcolor
    void DFS(int_t vtx, int_t lastcolor, int_t mask) {
        for (int_t i = graph[vtx].size() - 1; i >= 0 && i < graph[vtx].size();
             i--) {
            if (visited[graph[vtx][i].id] == false) {
                color[graph[vtx][i].id] |= (lastcolor ^ mask);
                visited[graph[vtx][i].id] = true;
                int_t to = graph[vtx][i].to;
                graph[vtx].pop_back();
                DFS(to, lastcolor ^ mask, mask);
            } else {
                graph[vtx].pop_back();
            }
        }
    }
    //当前对第bit位进行染色
    void process(int_t* left, int_t* right, int_t bit) {
        if (bit == -1) return;
        if (left > right) return;
        for (auto ptr = left; ptr <= right; ptr++) {
            const auto& edge = ::edges[*ptr];
            graph[edge.from].push_back(Edge{edge.to, edge.id});
            graph[edge.to].push_back(Edge{edge.from, edge.id});
        }
        for (auto ptr = left; ptr <= right; ptr++) {
            const auto& edge = ::edges[*ptr];
            if (visited[edge.id] == false) DFS(edge.from, 0, 1 << bit);
        }
        static int_t zeros[LARGE + 1], ones[LARGE + 1];
        zeros[0] = ones[0] = 0;
        for (auto ptr = left; ptr <= right; ptr++) {
            const auto& edge = ::edges[*ptr];
            graph[edge.from].clear();
            graph[edge.to].clear();
            if (color[edge.id] & (1 << bit))
                ones[++ones[0]] = *ptr;
            else
                zeros[++zeros[0]] = *ptr;
            visited[edge.id] = false;
        }
    
        std::copy(zeros + 1, zeros + 1 + zeros[0], left);
        auto last = left + zeros[0];
        std::copy(ones + 1, ones + 1 + ones[0], last);
        assert(last - left == right - last + 1);
        process(left, last - 1, bit - 1);
        process(last, right, bit - 1);
    }
    int_t gcd(int_t a, int_t b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }
    int main() {
        while (true) {
            scanf("%lld%lld", &n, &m);
            if (n == 0) break;
            for (int_t i = 1; i <= m; i++) {
                int_t from, to;
                scanf("%lld%lld", &from, &to);
                degree[from]++, degree[to]++;
                edges[i].to = to;
                edges[i].from = from;
                edges[i].id = i;
            }
            int_t ors = 0;
            for (int_t i = 1; i <= n; i++) ors = gcd(ors, degree[i]);
            int_t k = 0;
            while (ors % 2 == 0) {
                k++;
                ors /= 2;
            }
            for (int_t i = 1; i <= n; i++)
                if ((degree[i] & 1) || degree[i] < 2) k = -1;
            if (m & 1) k = -1;
            if (k == -1) {
                cout << -1 << endl;
                std::fill(degree + 1, degree + 1 + n, 0);
                std::fill(color + 1, color + 1 + m, 0);
                continue;
            }
            static int_t edges[LARGE + 1];
            for (int_t i = 1; i <= m; i++) edges[i] = i;
            process(edges + 1, edges + m, k - 1);
            printf("%lld\n", k);
            for (int_t i = 1; i <= m; i++) printf("%lld ", color[i] + 1);
            std::fill(degree + 1, degree + 1 + n, 0);
            std::fill(color + 1, color + 1 + m, 0);
            printf("\n");
        }
        return 0;
    }

     

  • noi.ac164 字符串游戏

    读错题引发的血案。

    题目里加粗的可以在字符串任意位置加入字符我没看到,然后GG了。

    当且仅当原串能够删除一个字符使得剩下的串为01相间的时候,先手必胜。

    可以转化为串中至多有一个位置存在两个连续的0或者1.

    首先考虑整个串都是01相间的情况,显然先手必胜。

    存在1处连续两个1的时候,设其为0110

    如果A先放置1,那么B不管如何放置,A总能放出连续两个1,。

    存在连续两个0时同理。

    存在多处连续两个1的时候,设其为

    0110110

    A在放置出第一个11的时候,B一定会采取策略阻止A放置第二个11(A之所以能放第一个11因为他是先手)。

    #include <cstring>
    #include <iostream>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 5000;
    char buf[LARGE * 2 + 1];
    bool prefix[LARGE + 1], suffix[LARGE + 1];
    int main() {
        int T;
        cin >> T;
        while (T--) {
            scanf("%s", buf + 1);
            int n = strlen(buf + 1);
            for (int i = 1; i <= n; i++) buf[i] -= '0';
            if (n <= 2) {
                cout << "Zhangzj" << endl;
                continue;
            }
            prefix[1] = true, suffix[n] = true;
            for (int i = 2; i <= n; i++)
                prefix[i] = prefix[i - 1] && (buf[i] ^ buf[i - 1]);
            for (int i = n - 1; i >= 1; i--)
                suffix[i] = suffix[i + 1] && (buf[i] ^ buf[i + 1]);
            bool result = false;
            for (int i = 2; i < n && (!result); i++) {
                result |=
                    (buf[i - 1] ^ buf[i + 1]) && prefix[i - 1] && suffix[i + 1];
            }
            result |= (prefix[n - 1] || suffix[2]);
            if (result)
                cout << "Zhangzj" << endl;
            else
                cout << "Owaski" << endl;
        }
        return 0;
    }

     

  • HNOI2019 白兔之舞

    $$ \text{首先考虑朴素}DP,\text{设}f\left( u,v,l \right) \text{表示走到点}\left( u,v \right) ,\text{路径长度为}l\text{的方案数} \\ \text{转移则有}f\left( u,v,l \right) =\sum_{0\le i<u}{\sum_{1\le j\le n}{f\left( i,j,l-1 \right) w\left( j,v \right)}} \\ \text{显然第三维状态可以用多项式表示。} \\ \text{令}F\left( u,v \right) \text{表示走到横坐标为}u,\text{纵坐标为}v\text{处的多项式} \\ \text{其中}i\text{次项表示路径长度为}i\left( \text{模}k\text{意义下} \right) \text{的方案数。} \\ \text{转移则有}F\left( u,v \right) =\sum_{0\le i<u}{\sum_{1\le j\le n}{w\left( j,v \right) xF\left( i,j \right)}} \\ =\sum_{1\le j\le n}{w\left( j,v \right) x\sum_{0\le i<u}{F\left( i,j \right)}} \\ \text{令}S\left( u,v \right) =\sum_{0\le i\le u}{F\left( i,v \right)} \\ \text{则有}F\left( u,v \right) =\sum_{1\le j\le n}{w\left( j,v \right) xS\left( u-\text{1,}v \right)} \\ \text{由于题目保证}k|\left( p-1 \right) ,\text{故在模}p\text{意义下存在}k\text{次单位根}g^{\frac{p-1}{k}},\text{故直接以单位根代入进去} \\ \text{即可做质数模}k\text{意义下的循环卷积。} \\ \text{每次代入一个点值}x_0,\text{则有} \\ F\left( u,v \right) =\sum_{1\le j\le n}{w\left( j,v \right) x_0S\left( u-\text{1,}v \right)} \\ \text{可以考虑直接矩乘。} \\ \text{例如对于}n=\text{3的情况,构造矩阵} \\ M_k=\left[ \begin{matrix} S\left( k,1 \right)& S\left( k,2 \right)& S\left( k,3 \right)\\ \end{matrix} \right] \\ \text{由于}S\left( k,1 \right) =F\left( k,1 \right) +S\left( k-\text{1,}1 \right) =S\left( k-\text{1,}1 \right) +\sum_{1\le j\le n}{w\left( j,1 \right) x_iS\left( k-\text{1,}j \right)} \\ \text{故构造转移矩阵} \\ T=\left[ \begin{matrix} w\left( \text{1,}1 \right) +1& w\left( \text{1,}2 \right)& w\left( \text{1,}3 \right)\\ w\left( \text{2,}1 \right)& w\left( \text{2,}2 \right) +1& w\left( \text{2,}3 \right)\\ w\left( \text{3,}1 \right)& w\left( \text{3,}2 \right)& w\left( \text{3,}3 \right) +1\\ \end{matrix} \right] \\ \text{由于题目要求可以在任意位置结束,故最终}M_L=M_0T^L\text{即为结果的矩阵。} \\ M_0\left( \text{1,}x \right) =\text{1,其中}x\text{为初始时的纵坐标}. \\ \text{单次求点值的复杂度为}n^3\log m,\text{共计需要}k\text{个点值来确定多项式。} \\ \text{然而很自闭的是}k\text{的长度不一定是2的幂,模数也不是}998244353 \\ \text{所以不能直接}NTT \\ \text{模数的问题比较好解决,}MTT\text{就行了} \\ \text{长度呢?} \\ \text{考虑 Bluestein 算法。} \\ \text{令}a_i\text{为多项式的}i\text{次项前的系数,}\omega _n=g^{\frac{p-1}{n}}\text{为模}p\text{意义下的}n\text{次单位根} \\ X_i\text{为代入}\omega _{n}^{i}\text{的结果,}n\text{为多项式的长度} \\ \text{则有}X_k=\sum_{0\le i\le n-1}{a_i\omega _{n}^{ik}}\left( DFT\text{的定义} \right) \\ \text{则有}X_k=\sum_{0\le i\le n-1}{a_i\omega _{n}^{ik}}=\sum_{0\le i\le n-1}{a_i\omega _{n}^{\frac{2ik}{2}}}=\sum_{0\le i\le n-1}{a_i\omega _{n}^{\frac{-\left( i-k \right) ^2+\left( i^2+k^2 \right)}{2}}} \\ =\sum_{0\le i\le n-1}{a_i\omega _{2n}^{-\left( i-k \right) ^2+\left( -i^2-k^2 \right)}}=\sum_{0\le i\le n-1}{a_i\omega _{2n}^{i^2+k^2}\omega _{2n}^{-\left( i-k \right) ^2}}=\omega _{2n}^{k^2}\sum_{0\le i\le n-1}{a_i\omega _{2n}^{i^2}\times \omega _{2n}^{-\left( k-i \right) ^2}} \\ \text{如果令}f\left( i \right) =a_i\omega _{2n}^{i^2},g\left( i \right) =\omega _{2n}^{-\left( i^2 \right)} \\ \text{则有}X_k=\omega _{2n}^{k^2}\sum_{0\le i\le n-1}{f\left( i \right) g\left( k-i \right)} \\ \text{然后自闭了,}k-i\text{可能是负的。} \\ \text{令}g\left( i \right) =\omega _{2n}^{-\left( i-n \right) ^2}\left( \text{即把}g\text{右移}n\text{位} \right) ,\text{同时把}g\left( i \right) \text{的长度扩大一倍,就避免了下标为负的情况。} \\ X_k=\omega _{2n}^{k^2}\sum_{0\le i\le n-1}{f\left( i \right) g\left( k-i \right)} \\ \text{然后会发现一分都没有,因为这题的所有数据都不满足}2k|p-1 \\ \text{所以只能写多点求值了}.. \\ \text{然后写了个多点求值发现}TLE\text{到0分。} \\ \text{还是得考虑Bluestein及其变种。} \\ \,\,X_k=\sum_{0\le i\le n-1}{\omega _{n}^{-ik}a_i} \\ \text{由于}\left( \begin{array}{c} a+b\\ 2\\ \end{array} \right) =ab+\left( \begin{array}{c} a\\ 2\\ \end{array} \right) +\left( \begin{array}{c} b\\ 2\\ \end{array} \right) \left( \text{直接考虑组合意义} \right) \\ ab=\left( \begin{array}{c} a+b\\ 2\\ \end{array} \right) -\left( \begin{array}{c} a\\ 2\\ \end{array} \right) -\left( \begin{array}{c} b\\ 2\\ \end{array} \right) \\ \,\,X_k=\sum_{0\le i\le n-1}{\omega _{n}^{-\left( \left( \begin{array}{c} i+k\\ 2\\ \end{array} \right) -\left( \begin{array}{c} i\\ 2\\ \end{array} \right) -\left( \begin{array}{c} k\\ 2\\ \end{array} \right) \right)}a_i}=\sum_{0\le i\le n-1}{\omega _{n}^{\left( \begin{array}{c} i\\ 2\\ \end{array} \right) +\left( \begin{array}{c} k\\ 2\\ \end{array} \right) -\left( \begin{array}{c} i+k\\ 2\\ \end{array} \right)}a_i} \\ =\omega _{n}^{\left( \begin{array}{c} k\\ 2\\ \end{array} \right)}\sum_{0\le i\le n-1}{\omega _{n}^{-\left( \begin{array}{c} k+i\\ 2\\ \end{array} \right)}\times a_i\omega _{n}^{\left( \begin{array}{c} i\\ 2\\ \end{array} \right)}} \\ \text{设序列}f\left( n \right) =\omega _{n}^{-\left( \begin{array}{c} n\\ 2\\ \end{array} \right)},g\left( n \right) =a_n\omega _{n}^{\left( \begin{array}{c} n\\ 2\\ \end{array} \right)} \\ \text{则有}X_k=\omega _{n}^{\left( \begin{array}{c} k\\ 2\\ \end{array} \right)}\sum_{0\le i\le n-1}{f\left( k+i \right) \times g\left( i \right)} \\ f,g\text{长度均为}n \\ \text{翻转}g\text{序列,则有}g^R\left( n-i \right) =g\left( i \right) \\ \text{则有}X_k=\omega _{n}^{\left( \begin{array}{c} k\\ 2\\ \end{array} \right)}\sum_{0\le i\le n-1}{f\left( k+i \right) \times g\left( n-i \right)} \\ \left( k+i \right) +\left( n-i \right) =n+k?\text{把}f\text{的长度扩大一倍即可。} \\ $$

    #include <assert.h>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <fstream>
    #include <iostream>
    #include <vector>
    // #include
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    using real_t = double;
    using cpx_t = struct complex;
    const int_t LARGE = 5e5;
    const real_t PI = acos(-1);
    struct complex {
        real_t real, imag;
        complex(real_t a = 0, real_t b = 0) : real(a), imag(b) {}
        complex operator+(const complex& rhs) const {
            return complex{real + rhs.real, imag + rhs.imag};
        }
        complex operator-(const complex& rhs) const {
            return complex{real - rhs.real, imag - rhs.imag};
        }
        complex operator*(const complex& rhs) const {
            return complex{real * rhs.real - imag * rhs.imag,
                           real * rhs.imag + imag * rhs.real};
        }
        complex& operator*=(const complex& rhs) {
            *this = (*this) * rhs;
            return *this;
        }
        complex conj() { return complex{real, -imag}; }
    };
    void transform(cpx_t* A, int size2, int arg);
    void poly_mul(const int* A, int n, const int* B, int m, int* A0, int p);
    // void transformX(int* A, int len, int g, int mod);
    void transformNTT(int* A, int size2, int arg, int mod, int g);
    std::vector<int> poly_dc_mul(const std::vector<int>& A);
    void poly_inv(const int* A, int n, int* result);
    void poly_div(const int* A, int n, const int* B, int m, int* R);
    std::vector<int> poly_eval(const std::vector<int>& poly,
                               const std::vector<int>& vals);
    void transformX(int* A0, int len, int g);
    int power(int_t base, int_t index, int_t mod) {
        int result = 1;
        // base = (base % mod + mod) % mod;
        index = (index % (mod - 1) + mod - 1) % (mod - 1);
        while (index) {
            if (index & 1) result = (int_t)result * base % mod;
            index >>= 1;
            base = (int_t)base * base % mod;
        }
        assert(result >= 0);
        return result;
    }
    int_t mod;
    struct Matrix {
        int_t n;
        int_t data[4][4];
        int_t* operator[](int_t r) { return data[r]; }
        int_t at(int_t r, int_t c) const { return data[r][c]; }
        Matrix(int_t n) {
            this->n = n;
            memset(data, 0, sizeof(data));
        }
        Matrix operator*(const Matrix& rhs) const {
            Matrix result(n);
            for (int_t i = 1; i <= n; i++) {
                for (int_t j = 1; j <= n; j++) {
                    for (int_t k = 1; k <= n; k++) {
                        result[i][j] =
                            (at(i, k) * rhs.at(k, j) % mod + mod + result[i][j]) %
                            mod;
                    }
                }
            }
            return result;
        }
        Matrix pow(int_t index) const {
            Matrix base = *this, result(n);
            for (int_t i = 1; i <= n; i++) result[i][i] = 1;
            while (index) {
                if (index & 1) result = result * base;
                base = base * base;
                index >>= 1;
            }
            return result;
        }
    };
    int_t n, k, l, x, y;
    int_t mat[4][4];
    Matrix makeMatrix(int_t x0) {
        Matrix result(n);
        for (int_t i = 1; i <= n; i++)
            for (int_t j = 1; j <= n; j++) {
                result[i][j] = x0 * mat[i][j] % mod;
                if (i == j) result[i][j] += 1;
                result[i][j] %= mod;
            }
        return result;
    }
    std::ostream& operator<<(std::ostream& os, const Matrix& mat) {
        for (int_t i = 1; i <= mat.n; i++) {
            for (int_t j = 1; j <= mat.n; j++) {
                os << mat.at(i, j) << " ";
            }
            cout << endl;
        }
        return os;
    }
    int flips[20][LARGE];
    const auto flip = [=](int x, int size2) {
        int result = 0;
        for (int i = 1; i < size2; i++) {
            result |= (x & 1);
            x >>= 1;
            result <<= 1;
        }
        return result | (x & 1);
    };
    int main() {
        for (int i = 1; i < 19; i++) {
            for (int j = 1; j < (1 << i); j++) flips[i][j] = flip(j, i);
        }
        cin >> n >> k >> l >> x >> y >> mod;
        for (int_t i = 1; i <= n; i++)
            for (int_t j = 1; j <= n; j++) cin >> mat[i][j];
        Matrix M0(n);
        static int A[LARGE + 1];
        M0[1][x] = 1;
        int_t g = 2;
        std::vector<int_t> divs;
        int_t px = mod - 1;
        assert(px % k == 0);
        for (int_t i = 2; i * i <= px; i++) {
            if (px % i == 0) {
                divs.push_back(i);
                if (i * i != px) divs.push_back(px / i);
            }
        }
        for (; g <= mod - 2; g++) {
            bool ok = true;
            for (int_t x : divs) {
                if (power(g, x, mod) == 1 && x != mod - 1) {
                    ok = false;
                    break;
                }
            }
            if (ok) break;
        }
        for (int_t i = 0; i < k; i++) {
            int_t gx = power(g, (mod - 1) / k * i, mod);
            auto T = makeMatrix(gx);
            A[i] = (M0 * T.pow(l))[1][y];
        }
        transformX(A, k, g);
        const int_t leninv = power(k, mod - 2, mod);
        for (int_t i = 0; i < k; i++) printf("%lld\n", A[i] * leninv % mod);
        return 0;
    }
    void transformX(int* A0, int len, int g) {
        const auto C2 = [](int_t n) -> int_t { return n * (n - 1) / 2; };
        int root = power(g, (mod - 1) / len, mod);
        static int A[LARGE + 1], B[LARGE + 1], A1[LARGE + 1];
        for (int i = 0; i <= len * 2; i++) {
            A[i] = power(root, -C2(i), mod);
        }
        for (int i = 0; i < len; i++) {
            B[i] = (int_t)A0[i] * power(root, C2(i), mod) % mod;
        }
        std::reverse(B, B + len + 1);
        poly_mul(A, len * 2, B, len, A1, mod);
        for (int i = 0; i < len; i++)
            A0[i] = (int_t)A1[i + len] % mod * power(root, C2(i), mod) % mod;
    }
    void poly_mul(const int* A, int n, const int* B, int m, int* A0, int p) {
        int size2 = 0;
        while ((1 << size2) < n + m + 1) size2++;
        int len = (1 << size2);
        const int px = 3e4;
        static cpx_t C[LARGE + 1], D[LARGE + 1], G[LARGE + 1], Px[LARGE + 1];
        for (int i = 0; i < len; i++) {
            if (i <= n) {
                C[i] = cpx_t{A[i] / px};
                D[i] = cpx_t{A[i] % px};
            } else {
                C[i] = D[i] = cpx_t();
            }
            if (i <= m) {
                G[i] = cpx_t{B[i] / px, B[i] % px};
            } else {
                G[i] = cpx_t{0, 0};
            }
        }
        transform(C, size2, 1);
        transform(D, size2, 1);
        transform(G, size2, 1);
        for (int_t i = 0; i < len; i++) {
            C[i] = C[i] * G[i];
            D[i] = D[i] * G[i];
        }
        transform(C, size2, -1);
        transform(D, size2, -1);
    
        const auto make = [=](real_t x) -> int_t {
            assert(x / len >= -1);
            return (x / len) + 0.5;
        };
        for (int i = 0; i <= n + m; i++) {
            A0[i] = ((int_t)make(C[i].real) % p * px % p * px % p +
                     (int_t)make(C[i].imag + D[i].real) % p * px % p +
                     make(D[i].imag) % p) %
                    p;
        }
    }
    void transform(cpx_t* A, int size2, int arg) {
        int len = (1 << size2);
    
        for (int i = 0; i < len; i++) {
            int x = flips[size2][i];
            if (x > i) std::swap(A[i], A[x]);
        }
        for (int i = 2; i <= len; i *= 2) {
            for (int j = 0; j < len; j += i) {
                for (int k = 0; k < i / 2; k++) {
                    auto u = A[j + k],
                         t = cpx_t(cos(2 * PI / i * k), sin(2 * PI / i * k) * arg) *
                             A[j + k + i / 2];
                    A[j + k] = u + t;
                    A[j + k + i / 2] = u - t;
                }
            }
        }
    }

     

  • noi.ac180 divisors

    $$ \sum_{1\le i\le n}{\sum_{d|i}{gcd\left( d,\frac{i}{d} \right)}} \\ =\sum_{1\le x\le n}{x\sum_{1\le i\le n}{\sum_{d|i}{\left[ gcd\left( d,\frac{i}{d} \right) =x \right]}}} \\ =\sum_{1\le x\le n}{x\sum_{1\le i\le \lfloor \frac{n}{x^2} \rfloor}{\sum_{d|i}{\left[ gcd\left( d,\frac{i}{d} \right) =1 \right]}}} \\ =\sum_{1\le x\le n}{x\sum_{1\le i\le \lfloor \frac{n}{x^2} \rfloor}{\sum_{d|i}{\sum_{k|d,k|\frac{i}{d}}{\mu \left( k \right)}}}} \\ =\sum_{1\le x\le n}{x\sum_{1\le k\le \lfloor \frac{n}{x^2} \rfloor}{\mu \left( k \right)}\sum_{1\le i\le \lfloor \frac{n}{k^2x^2} \rfloor}{\sum_{d|i}{1}}} \\ \text{设}f\left( x \right) =\sum_{1\le i\le x}{\sum_{d|i}{1}}=\sum_{1\le i\le x}{\lfloor \frac{x}{i} \rfloor} \\ \text{则有}\sum_{1\le x\le n}{x\sum_{1\le k\le \lfloor \frac{n}{x^2} \rfloor}{\mu \left( k \right) f\left( \lfloor \frac{n}{k^2x^2} \rfloor \right)}} \\ \text{令}T=kx,\text{则有} \\ \sum_{\,\,1\le T\le n}{f\left( \lfloor \frac{n}{T^2} \rfloor \right) \sum_{k|T}{\mu \left( k \right) \frac{T}{k}}}=\sum_{1\le T\le n}{f\left( \lfloor \frac{n}{T^2} \rfloor \right) \varphi \left( T \right)} \\ \text{由于}T^2\le n,\text{故}T\le \sqrt{n} \\ \text{则有}\sum_{1\le T\le \sqrt{n}}{f\left( \lfloor \frac{n}{T^2} \rfloor \right) \varphi \left( T \right)} \\ \text{复杂度}O\left( \sum_{1\le i\le \sqrt{n}}{\sqrt{\frac{n}{i^2}}} \right) =O\left( \sqrt{n}\log n \right) \\ \\ $$

    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <iostream>
    #include <vector>
    using int_t = long long int;
    using std::cin;
    using std::cout;
    using std::endl;
    const int_t LARGE = 5e6;
    bool isPrime[LARGE + 1];
    std::vector<int_t> primes;
    int_t factor[LARGE + 1], phi[LARGE + 1];
    int_t f(int_t n) {
        int_t i = 1, result = 0;
        // int_t px = n;
        while (i * i <= n) {
            result += (n / i);
            // px--;
            i++;
        }
        int_t k = n / i;
        while (i <= n) {
            // int_t t
            int_t next = n / k;
            result += k * (next - i + 1);
            i = next + 1;
            k--;
        }
        return result;
    }
    int main() {
        int_t n;
        cin >> n;
        int_t sqrt = ::sqrt(n) + 1;
        memset(isPrime, 1, sizeof(isPrime));
        phi[1] = 1;
        for (int_t i = 2; i <= sqrt; i++) {
            if (isPrime[i]) {
                factor[i] = i;
                primes.push_back(i);
            }
            for (int_t x : primes) {
                if (i * x > sqrt) break;
                isPrime[i * x] = false;
                factor[i * x] = x;
                // factor[i * x] = x;
                if (i % x == 0) break;
            }
        }
    #ifdef DEBUG
        for (int_t x : primes) cout << x << endl;
    #endif
        phi[1] = 1;
        for (int_t i = 2; i <= sqrt; i++) {
            int_t p = factor[i];
            if (i / p % p == 0)
                phi[i] = phi[i / p] * p;
            else
                phi[i] = phi[i / p] * (p - 1);
        }
        int_t result = 0;
        for (int_t i = 1; i * i <= n; i++) {
            result += f(n / (i * i)) * phi[i];
    #ifdef DEBUG
    
            cout << "phi " << i << " = " << phi[i] << endl;
    
    #endif
        }
        cout << result << endl;
        return 0;
    }