资料整理

使用再生龙还原镜像文件到硬盘 1. 把制作好的再生龙光盘插入需要的电脑,开机按F4使用再生龙启动2. 选择默认的:Clonezailla live(Default settings,VGA 800*600),然后等待几分钟 3. 选择语言:简体中文 4. 选择不修改键盘映射 5. 选择使用再生龙          6. 选择硬盘/分区(存到/来自)镜像文件,此选项既可以制作镜像,也可以恢复镜像。 7. 使用本机的分区     ... 阅读详情

1.区间最大值

求数组(连续)在某一区间最大值(简单的动态规划的思想)

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int main(){
    int n;
    int a[100];
    while(~scanf("%d", &n)){
        int i, sum = 0;
        for(i = 0; i < n; i++){
            cin>>a[i];
        }
        int max1 = 0;
        for(i = 0; i < n; i++){
            sum += a[i];
            max1 = max(sum, max1);
            if(sum < 0)sum = 0;
        }
        cout<<max1<<endl;
    }
return 0;
}

2.N!问题

2.1N!是质因数分解:

x = [ n / p ] + [ n / p ∧ 2 ] + [ n / p ∧ 3 ] + ⋯ ⋯ ⋯ \mathrm{x}=[\mathrm{n} / \mathrm{p}]+\left[\mathrm{n} / \mathrm{p}^{\wedge} 2\right]+\left[\mathrm{n} / \mathrm{p}^{\wedge} 3\right]+\cdots \cdots \cdots x=[n/p]+[n/p∧2]+[n/p∧3]+⋯⋯⋯
例如:给你N,问你N!有几个0.我们可有求出2的个数n, 5的个数m,res = min(n, m), n和m的求法按照上面的公式。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
int a[maxn], vis[maxn];
int cn = 1;
void Prime()
{
    memset(vis, 0, sizeof(maxn));
    for(int i = 2; i <= maxn; i++)
    {
        if(!vis[i])
        {
            a[cn++] = i;
        }
        for(int j = 1; j < cn && i*a[j] <= maxn; j++)
        {
            vis[i* a[j]] = 1;
            if(i % a[j] == 0)break;
        }
    }
}
int query(int x, int n)//对应x的公式
{
    int ans= 0;
    while(n)
    {
        ans += n / x;
        n /= x;
    }
    return ans;
}
int main()
{
    int n;
    Prime();
    int T, ca = 0;
    scanf("%d", &T);
    while(T--)
    {
        cin>>n;
        printf("Case %d: %d =", ++ca, n);
        for(int i = 1; i < cn; i++)
        {
            int t = query(a[i], n);
            if(t == 0)break;
            if(i != 1)printf(" *");
            printf(" %d (%d)", a[i], t);
 
        }
        printf("\n");
 
    }
    return 0;
}

2.2求出N,把他转化为base禁制, N!是几位的。

N! = base^k,这个应该能想明白吧。然后等式两边同时取对数也就是log(N!) = k*log(base),然后log(N!) = log(1)+log(2)+…+log(n),log(N!)/k就是最后的结果了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
double sum[maxn];
void Init()
{
    for(int i = 2; i <= maxn; i++)//预处理
    {
        sum[i] += sum[i-1]+log(double(i));
    }
}
int main()
{
    int T, n, base;
    Init();
    cin>>T;
    int t, ca = 0;
    while(T--)
    {
        cin>>n>>base;
        if(n == 0)printf("Case %d: 1\n",++ca);
        else
        {
            double ans =sum[n];
            ans /= log((double) base);
            if(ans != (int)ans)t = (int)ans + 1;
            else t = (int)ans;
            printf("Case %d: %d\n",++ca, t );
        }
    }
 
    return 0;
}

3. 1~N的约数问题

3.1 1~n所有数的约数之和:

暴力的方法就是n/i*i;
例题:首先给你个函数SOD(n)为除了1和本身所有的因子之和,如果则会个数的因子是一样的话只能算一个,比如4的因子就是一个2,然后在给你一个函数CSOD(n)它为SOD(n)从1到n的和。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
int main()
{
    LL ans, a;
    int T;
    cin>>T;
    int ca= 0;
    while(T--)
    {
        ans = 0;
        cin>>a;
        int m = sqrt(a);
        LL t1, t2 = m+1;;
        for(int i = 2; i <= m; i++)
        {
            t1 = a / i;
            ans += (t1-1)*i;//减1的情况就是这个因子本身
            if(t1 >= t2)
            {
                ans += (t1-t2+1)*(t1+t2)/2;
//当为2的时候算的是从11到50的值,当i为3的时候算的是从11到33的值。。。
//发没发现34~50之间的数只算了一边。。。。
            }
        }
        printf("Case %d: %lld\n",++ca, ans);
    }
    return 0;
}

3.2 1~n所有数的约束的个数

int main()
{
    int n;
    cin>>n;
    long ans=0;
    int i;
    for(i=1; i*i<=n; i++)
    {
        ans+=n/i;
    }
    ans+=(n-i+1);//当i*i> n以后因数只剩下一个
    cout<<ans<<endl;
}

4. 大数取模(同余模定理)

被除数是一个非常大的数,除数最大是一个长整型的数

#include<bits/stdc++.h>
using namespace std;
#define LL long long
char a[205];
int main()
{
    int T;
    scanf("%d", &T);
    int ca = 0;
    while(T--)
    {
        getchar();
        LL n;
        scanf("%s %lld", a, &n);
        int len = strlen(a);
        printf("Case %d: ", ++ca);
        if(a[0] == '0' && len == 1)
        {
            printf("divisible\n");
            continue;
        }
        if(n < 0)n = -n;
        LL ans = 0;
        int i = 0;
        if(a[0] == '-')i = 1;
        for(; i < len; i++)
        {
            ans  = (ans*10+a[i]-'0')%n;
        }
        if(ans == 0)printf("divisible\n");
        else printf("not divisible\n");
    }
    return 0;
}

5.求Hn

H n = 1 + 1 2 + 1 3 + 1 4 + ⋯ + 1 n = ∑ k = 1 n 1 k H_{n}=1+\frac{1}{2}+\frac{1}{3}+\frac{1}{4}+\cdots+\frac{1}{n} =\sum_{k=1}^{n} \frac{1}{k} Hn​=1+21​+31​+41​+⋯+n1​=k=1∑n​k1​
方法一(分块打表):题意十分的简单,但是看到这道题第一想法就是暴力,后来发现有T次查询,如果暴力指定超时,所以就想到了打表,打表的时间复杂度是O(n),查询时O(1),但是问题来了数据范围最大到1e8,内存会爆掉的,所以就需要优化一下节约内存,所以可以采用分段打表,100数打一个表,空间是1e6那么内存就不会。

#include<bits/stdc++.h>
using namespace std;
const int maxn  = 1e6+5;
const int INF = 1e8+5;
double a[maxn];
void init(){//打表
    double tmp = 0;
    for(int i = 1; i <= INF; i++){
        tmp += double(1.0/i);
        if(i % 100  == 0){
            a[i/100] = tmp;
        }
    }
}
int main(){
    int T;
    init();
    cin>>T;
    int ca = 0;
    while(T--){
        int n;
        scanf("%d", &n);
        int t = n / 100;//算出n在那一块
        double ans = a[t];
        for(int i = t * 100+1; i <= n; i++){//剩下的部分
            ans += 1.0/(i*1.0);
        }
        printf("Case %d: %.10lf\n",++ca, ans);
    }
return 0;
}

方法二(公式法):
f(n)=ln(n)+C+1/(2*n), 公式中C≈0.57721566490153286060651209;但是使用的时候n不能过小, 10000以上的数就可以用这个公式,剩下的就是打表,10000以内的数打表应该非常容易。

#include<bits/stdc++.h>
using namespace std;
const double  C = 0.57721566490153286060651209;
const int maxn = 100000;
double a[maxn];
void Init(){
     a[1] = 1;
     for(int i = 2; i <= maxn; i++){
        a[i] = a[i-1]+double(1.0/i);
     }
}
int main(){
    int T;
    Init();
    cin>>T;
    int ca = 0;
    while(T--){
        int n;
        scanf("%d", &n);
        if(n <= maxn)printf("Case %d: %.10lf\n",++ca, a[n]);
        else {
            printf("Case %d: %.10lf\n",++ca, log(n)+C+double(1.0/(2*n)));
        }
    }
 
return 0;
}

6.求n^k的前三位数字与后三位数字

两个数n、k,求n^k的前三位数字与后三位数字。
题目解析:后三位数字很容易求,通过幂取模即可。先考虑n不做幂运算时如何取前三位,令d=log10(n),则d=a(整数部分)+i(小数部分)
n = 1 0 d = 1 0 a + i = 1 0 a ∗ 1 0 i \mathrm{n}=10^{d}=10^{a+i}=10^\mathrm{a} * 10^ \mathrm{i} n=10d=10a+i=10a∗10i
则 1 0 i ∗ 100 10^{i}*100 10i∗100即为前三位。以123456为例,d=log10(123456)=5+i,则 123456 = 1 0 i ∗ 1 0 5 123456=10^i*10^5 123456=10i∗105又因为 123456 = 1.23456 ∗ 1 0 5 123456=1.23456*10^5 123456=1.23456∗105所以 1 0 i = 1.23456 10^i=1.23456 10i=1.23456所以前三位为 1 0 i ∗ 100 = 123 10^i*100=123 10i∗100=123
再来考虑n做幂运算时,将 n k n^k nk变换为 1 0 k l o g 10 ( n ) 10^{klog10(n)} 10klog10(n)则 d = k ∗ l o g 10 ( n ) d=k*log10(n) d=k∗log10(n)

#include<string.h>
#include<bits/stdc++.h>
using namespace std;
#define LL long long
int q_pow(LL a, LL b){
   LL base = a, ans = 1;
   while(b){
       if(b & 1)ans = (ans * base) % 1000;
       base = (base * base) % 1000;
       b >>= 1;
   }
   return ans;
}
int main(){
    int T;
    cin>>T;
    int ca = 0;
    while(T--){
        LL n, k;
        scanf("%lld %lld", &n, &k);
        int ans1 = q_pow(n, k);
        double d = k * log10(n);
        int ans2 = pow(10, (d-(int)d))*100;
        printf("Case %d: %d %03d\n",++ca, ans2, ans1);
 
    }
}

7.莫队算法

#include<bits/stdc++.h>
using namespace std;
const int maxn = 50005;
int a[maxn];
struct Node{
      int l, r, i;
}pos[maxn];
int ans = 0, block;
int cnt[maxn], res[maxn];
void Add(int x){//先减去之前的,更新一下,加上现在的
     ans -= cnt[a[x]]*cnt[a[x]];
     ++cnt[a[x]];
     ans += cnt[a[x]] * cnt[a[x]];
}
void Remove(int x){
     ans -= cnt[a[x]]*cnt[a[x]];
     --cnt[a[x]];
     ans += cnt[a[x]] * cnt[a[x]];
}
 inline int read(){
     int k=0;
     char c;
     c=getchar();
     while(!isdigit(c))c=getchar();
     while(isdigit(c)){k=(k<<3)+(k<<1)+c-'0';c=getchar();}
     return k;
 }
bool cmp(Node A, Node B){
     if(A.l / block == B.l/ block)return A.r < B.r;
     else return A.l < B.l;
}
int main(){
    int n, m, k;
    n = read();
    m = read();
    k = read();
    for(int i = 1; i <= n; i++){
        a[i] = read();
    }
        block = sqrt(n);
    for(int i = 1; i <= m; i++){
        pos[i].l = read();
        pos[i].r = read();
        pos[i].i = i;
    }
    sort(pos+1, pos+m+1, cmp);
    int L = 1, R = 0;
    ans = 0;
    for(int i = 1; i <= m; i++){
        while(L < pos[i].l){
            Remove(L++);
        }
        while(L > pos[i].l){
            Add(--L);
        }
        while(R < pos[i].r){
            Add(++R);
        }
        while(R > pos[i].r){
            Remove(R--);
        }
       // cout<<ans<<endl;
        res[pos[i].i] = ans;
    }
    for(int i = 1; i <= m; i++){
        printf("%d\n", res[i]);
    }
return 0;
}

8.AC自动机

8.1给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+7;
 
int trie[maxn][26];
int word[maxn];
int fail[maxn];
int cnt = 0;
void InsertWord(string s){
     int root = 0;
     for(int i = 0; i < s.size(); i++){
        int next = s[i] - 'a';
        if(!trie[root][next]){
            trie[root][next] = ++cnt;
        }
        root = trie[root][next];
     }
     word[root]++;
}
void getFail(){
     queue<int>q;
     for(int i = 0; i < 26; i++){
        if(trie[0][i]){
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
     }
     while(!q.empty()){
          int now = q.front();
          q.pop();
          for(int i = 0; i < 26; i++){
            if(trie[now][i]){
                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else trie[now][i] = trie[fail[now]][i];
          }
     }
}
int query(string s){
    int now = 0, ans = 0;
    for(int i = 0; i < s.size(); i++){
        now = trie[now][s[i]-'a'];
        for(int j = now; j  && word[j] != -1; j = fail[j]){
            ans += word[j];
            word[j] = -1;
        }
    }
    return ans;
}
int main(){
    ios::sync_with_stdio(false);
    int n;
    string a, s;
    cin>>n;
    for(int i = 1; i <= n; i++){
        cin>>a;
        InsertWord(a);
    }
    fail[0] = 0;
    getFail();
    cin>>s;
    cout<<query(s)<<endl;
return 0;
}

8.2有N个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串T中出现的次数最多。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100000;
struct Node
{
    int id, cn;
} re[maxn];
string a[200];
int trie[maxn][26];
int word[maxn];
int fail[maxn];
int cnt = 0;
map<int, int>mp;//映射到是哪一个串
void InsertWord(string s, int i)
{
    int root = 0;
    for(int i = 0; i < s.size(); i++)
    {
        int next = s[i] - 'a';
        if(!trie[root][next])
        {
            trie[root][next] = ++cnt;
        }
        root = trie[root][next];
    }
    word[root]++;
    mp[root] = i;
}
void getFail()
{
    queue<int>q;
    for(int i = 0; i < 26; i++)
    {
        if(trie[0][i])
        {
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        for(int i = 0; i < 26; i++)
        {
            if(trie[now][i])
            {
                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else trie[now][i] = trie[fail[now]][i];
        }
    }
}
void query(string s)
{
    int now = 0, ans = 0;
    for(int i = 0; i < s.size(); i++)
    {
        now = trie[now][s[i]-'a'];
        for(int j = now; j; j = fail[j])
        {
            if(word[j])
            {
                int t = mp[j];
                re[t].id = t;
                re[t].cn++;
            }
 
        }
    }
}
bool cmp(Node A, Node B)
{
    if(A.cn == B.cn)
    {
        return A.id < B.id;
    }
    else return A.cn > B.cn;
}
void init(){//初始化
     cnt = 0;
     memset(word, 0, sizeof(word));
     memset(re, 0, sizeof(re));
     memset(fail, 0, sizeof(fail));
     memset(trie, 0, sizeof(trie));
}
int main()
{
    ios::sync_with_stdio(false);
    int n;
    string s;
    while(cin>>n && n != 0)
    {
 
        init();
        for(int i = 1; i <= n; i++)
        {
            cin>>a[i];
            InsertWord(a[i], i);
        }
        fail[0] = 0;
        cin>>s;
        getFail();
        query(s);
        sort(re+1, re+1+n, cmp);
        int ans = re[1].cn;
        cout<<ans<<endl;
        for(int i = 1; i <= n; i++)
        {
            if(re[i].cn == ans)
            {
                cout<<a[re[i].id]<<endl;
            }
            else break;
        }
    }
    return 0;
}

10.给你一个数n求LCM(1, 2,…,n ),LCM 为最大公约数

思路:位图+二分+欧拉筛+二分

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 1e8+5;
unsigned int pri[5800000];
const long long mod = 1LL<<32;
unsigned int sum[5800000];
bitset<maxn>vis;//位图压缩空间
int cn = 1;
void Prime(){//欧拉筛
     pri[0] = 0;
     for(int i = 2; i < maxn; i++){
        if(!vis[i])pri[cn++] = i;
        for(int j = 1; j < cn && i * pri[j] < maxn; j++){
            vis[i*pri[j]] = true;
            if(i % pri[j] == 0)break;
        }
     }
     sum[0] = 1;
     for(int i = 1; i < cn; i++){//前缀积
        sum[i] = sum[i-1]*pri[i];
     }
    // pri[cn] = maxn;
}
LL q_pow(int a, int b){//快速幂
   LL ans = 1, base = a;
   while(b){
       if(b & 1)ans = (ans * base) % mod;
       base = (base * base) % mod;
       b >>= 1;
   }
return ans;
}
int main(){
    Prime();
    int T;
    scanf("%d", &T);
    int ca = 0;
    //cout<<cn<<endl;
    while(T--){
        int n;
        scanf("%d", &n);
        int pos = upper_bound(pri+1, pri+cn, n) - pri-1;//二分查找第一个比n大的数,然后减去1,就小于等于n
        LL ans = sum[pos] % mod;
        for(int i = 1; i < cn && pri[i]*pri[i] <= n; i++){//
            int t = n;
            int k = 0;
            while(t / pri[i]){
                t /= pri[i];
                k++;
            }
            ans = q_pow(pri[i], --k)*ans % mod;
        }
        printf("Case %d: %lld\n",++ca, ans%mod);
    }
 
return 0;
}

11.二维前缀和

二维前缀和+二分+超级快读挂
本题的题意就是给你一个矩阵,只包含0或者1,然后让你求出最多只包含一个1的矩阵最大有多大。
思路:一开始看到这道题之后,感觉像搜索问题仔细寻思了一下发现不是,然后就想到了二维前缀和,求出二维前缀和之后,遍历所有的点,以(i, j)为正方形的右下角去二分查找,最大正方形的边长,然后找到最大的边长。但是这道题会卡你的数据,所以需要快速读入。

#include<bits/stdc++.h>
const int maxn = 1005;
int a[maxn][maxn], sum[maxn][maxn];
int n, m;
namespace fastIO
{
#define BUF_SIZE 100000
//fread -> read
bool IOerror = 0;
inline char nc()
{
    static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
    if(p1 == pend)
    {
        p1 = buf;
        pend = buf + fread(buf, 1, BUF_SIZE, stdin);
        if(pend == p1)
        {
            IOerror = 1;
            return -1;
        }
    }
    return *p1++;
}
inline bool blank(char ch)
{
    return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
inline void read(int &x)
{
    char ch;
    while(blank(ch = nc()));
    if(IOerror) return;
    for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
#undef BUF_SIZE
};
using namespace fastIO;
int Judge(int i, int j, int d)//二分查找
{
    int l = 1, r = d, mid, ans = 1;
    while(l < r)
    {
        int mid = (l + r + 1)>>1;
        if (sum[i][j]-sum[i-mid][j]-sum[i][j-mid]+sum[i-mid][j-mid] <= 1)//注意题意最多一个
        {
            l = mid;
        }
        else r = mid-1;
    }
    return l;
}
using namespace std;
int main()
{
    int T;
    read(T);
    while(T--)
    {
        read(n);
        read(m);
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= m; j++)
            {
                read(a[i][j]);
                sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
            }
        }
        int ans = 0;
        for(int i = 1; i <= n; i++)//遍历所有的点
        {
            for(int j = 1; j <= m; j++)
            {
                int d = min(i, j);//以(i, j)为右下角的正方形的边长最大为d
                int t = Judge(i, j, d);//
                ans = max(ans, t);
            }
        }
        printf("%d\n", ans);

    }
    return 0;
}

12.强连通分量

在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。

#include<bits/stdc++.h>
using namespace std;
 
const int maxn = 5010;
const int N =  50010;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
set<int>g[maxn];
set<int>::iterator it;
int ans = 0;
struct edge
{
    int v, next;
} e[N];
void add(int u, int v)
{
    e[k].v = v;
    e[k].next = head[u];
    head[u] = k++;
}
void tarjan(int u)
{
    int v;
    dfn[u] = low[u] = ++idx;
    ins[u] = 1;
    s.push(u);
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        v = e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(ins[v])low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            g[Bcnt].insert(v);
        }
        while(u != v);
        int  f = g[Bcnt].size();
        ans = max(f, ans);
    }
}
void init()
{
    idx = 0, k = 1, Bcnt = 0, ans = 0;
    memset(ins, 0, sizeof(ins));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0,sizeof(low));
    memset(head, -1, sizeof(head));
}
int main()
{
    int T, p, u, v, choose;
    init();
    scanf("%d %d", &n, &p);
    for(int i = 1; i <= p; i++)
    {
        scanf("%d %d %d", &u, &v, &choose);
        add(u, v);
        if(choose == 2)add(v, u);
    }
    for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
    int mi = INT_MAX, pos = 0;
    cout<<ans<<endl;
    for(int i = 1; i <= Bcnt; i++)
    {
        if(g[i].size() == ans)
        {
            int first_num = *(g[i].begin());
            if(mi > first_num)
            {
                pos = i;
                mi = first_num;
            }
        }
    }
    int t = 0;
    for(it = g[pos].begin(); it != g[pos].end(); it++)
    {
        if(t != 0)cout<<" ";
        printf("%d", *it);
        t++;
    }
    cout<<endl;
    return 0;
}

题意:就是有N头牛,给你M中关系,问你有多少头牛受欢迎,当一个牛被除了自己本身的所有牛喜欢那么这头牛就是最受欢迎的牛。
题解:本题采用Tarjan+缩点,把强连通分量看成一个点,统计出度为0的点有多少个,如果出度为0的点为一个的话,那么则这个点(可能是强连通分量缩的点)就是最受欢的,输出这个点包含多少头牛。如果出度为0的点有两个以上,那么就不存在最受欢迎的牛。

#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 10010;
const int N = 50010;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
int num[maxn];
int degree[maxn];
 
struct edge
{
    int v, next;
} e[N];
void add(int u, int v)
{
    e[k].v = v;
    e[k].next = head[u];
    head[u] = k++;
}
void tarjan(int u)
{
    int v;
    dfn[u] = low[u] = ++idx;
    ins[u] = 1;
    s.push(u);
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        v = e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(ins[v])low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            belong[v] = Bcnt;
            num[Bcnt]++;
 
        }
        while(u != v);
    }
}
void init()
{
    idx = 0, k = 1, Bcnt = 0;
    memset(ins, 0, sizeof(ins));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0,sizeof(low));
    memset(head, -1, sizeof(head));
    memset(num, 0, sizeof(num));
    memset(belong , 0, sizeof(belong));
    memset(degree, 0, sizeof(degree));
}
int main()
{
    int T, p, u, v, choose;
    init();
    scanf("%d %d", &n, &p);
    for(int i = 1; i <= p; i++)
    {
        scanf("%d %d", &u, &v);
        add(u, v);
    }
    for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
    for(int i = 1; i <= n; i++){
        for(int j = head[i]; j != -1; j = e[j].next){
            int v = e[j].v;
            if(belong[i] != belong[v]){
                degree[belong[i]]++;
            }
        }
    }
    int flag = 0, pos = 0;
    for(int i = 1; i <= Bcnt; i++){
        if(degree[i] == 0){
                flag++;
                pos = i;
        }
    }
    if(flag > 1)printf("0\n");
    else printf("%d\n", num[pos]);
    return 0;
}

缩点+DP
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 10010;
const int N = 500100;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0, cn = 0, ans;
int head[N], head1[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn], point[maxn], w[maxn], degree[maxn];
int dis[maxn];
 
struct edge
{
    int v, next;
} e[N];
struct edge_
{
    int v, next;
} e_[N];
void add(int u, int v)
{
    e[k].v = v;
    e[k].next = head[u];
    head[u] = k++;
}
void add_(int u, int v)
{
    e_[cn].v = v;
    e_[cn].next = head1[u];
    head1[u] = cn++;
}
void tarjan(int u)//强连通分量
{
    int v;
    dfn[u] = low[u] = ++idx;
    ins[u] = 1;
    s.push(u);
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        v = e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(ins[v])low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            belong[v] = Bcnt;
        }
        while(u != v);
    }
}
void init()
{
    idx = 0, k = 1, Bcnt = 0, cn = 1, ans = -1;
    memset(head1, -1,sizeof(head1));
    memset(head, -1, sizeof(head));
 
}
void TopSort()//拓扑排序
{
     queue<int>q;
     for(int i = 1; i <= Bcnt; i++){
        if(!degree[i])q.push(i);
        dis[i] = w[i];
     }
     while(!q.empty()){
        int x = q.front();
        q.pop();
        for(int i = head1[x]; i != -1; i = e_[i].next){
            int v = e_[i].v;
            dis[v] = max(dis[v], dis[x]+w[v]);
            degree[v]--;
            if(degree[v] == 0)q.push(v);
        }
     }
     for(int i = 1; i <= Bcnt; i++){
        ans = max(ans, dis[i]);
     }
 
 
}
int main()
{
    int T, p, u, v, choose;
    init();
    scanf("%d %d", &n, &p);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &point[i]);
    }
    for(int i = 1; i <= p; i++)
    {
        scanf("%d %d", &u, &v);
        add(u, v);
    }
    for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
    for(int i = 1; i <= n; i++)
    {
        int v;
        for(int j = head[i]; j != -1; j = e[j].next)
        {
            v = e[j].v;
            if(belong[i] != belong[v])//缩点
            {
                add_(belong[i], belong[v]);
                degree[belong[v]]++;
            }
        }
        w[belong[i]] += point[i];
    }
    TopSort();
    printf("%d\n", ans);
    return 0;
}

如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
思路:本题的思路就是,如果当这个人不能被贿赂并且这个人跟其他人没有关系,那么就是NO。剩下的就是利用缩点,把环缩成一个点,那么这个点的最小值就是整个点的价值。

#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 10010;
const int N = 50010;
const int INF = 1e9+100;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0;
int head[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn];
int num[maxn];
int degree[maxn], money[maxn];
 
struct edge
{
    int v, next;
} e[N];
void add(int u, int v)
{
    e[k].v = v;
    e[k].next = head[u];
    head[u] = k++;
}
void tarjan(int u)
{
    int v;
    dfn[u] = low[u] = ++idx;
    ins[u] = 1;
    s.push(u);
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        v = e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(ins[v])low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            belong[v] = Bcnt;
            num[Bcnt] = (num[Bcnt], money[v]);//更新环的最小值
 
        }
        while(u != v);
    }
}
void init()
{
    idx = 0, k = 1, Bcnt = 0;
    memset(ins, 0, sizeof(ins));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0,sizeof(low));
    memset(head, -1, sizeof(head));
    memset(num, 0, sizeof(num));
    memset(belong, 0, sizeof(belong));
}
int main()
{
    int T, p, u, v, choose;
    init();
    scanf("%d %d", &n, &p);
    for(int i = 1; i <= n; i++)money[i] = INF;
    for(int i = 1; i <= p; i++)
    {
        int pos, val;
        scanf("%d %d", &pos, &val);
        money[pos] = val;
    }
    int m;
    scanf("%d", &m);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d %d", &u, &v);
        add(u, v);
    }
    for(int i= 1; i <= n; i++)if(!dfn[i] && money[i] != INF)tarjan(i);
    for(int i = 1; i <= n; i++)
    {
        if(!dfn[i]) //如果这个人没有被贿赂,或者跟其他人没有关系
        {
            printf("NO\n");
            printf("%d\n", i);
            return 0;
        }
    }
    int ans = 0;
    for(int i = 1; i <= n; i++)
    {
        for(int j = head[i]; j != -1; j = e[j].next)
        {
            int v = e[j].v;
            if(belong[i] != belong[v])
            {
                degree[belong[v]]++;
            }
        }
    }
    for(int i = 1; i <= Bcnt; i++)
    {
        if(!degree[i]) //利用缩点,找到入度为0的点,
        {
            ans += num[i];
        }
    }
    printf("YES\n");
    printf("%d\n", ans);
    return 0;
}

割点:
给出一个n个点,m条边的无向图,求图的割点。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20010;
const int N = 200100;
int head[N];
int dfn[maxn], low[maxn], k = 0, tot = 0, root, cut[maxn];
struct Node
{
    int v, next;
} e[N];
void add(int u, int v)
{
    e[k].v = v;
    e[k].next = head[u];
    head[u] = k++;
}
void Tarjan(int x)
{
    dfn[x] = low[x] = ++tot;
    int flag = 0;
    for(int i = head[x]; i != -1; i = e[i].next)
    {
        int y = e[i].v;
        if(!dfn[y])
        {
            Tarjan(y);
            low[x] = min(low[x], low[y]);
            if(dfn[x] <= low[y] && (x != root || ++flag > 1))cut[x] = 1;
        }
        else low[x] = min(low[x], dfn[y]);
    }
}
int main()
{
    memset(head, -1, sizeof(head));
    int n, m, a, b;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d %d", &a, &b);
        add(a, b);
        add(b, a);
    }
    for(int i = 1; i <= n; i++)
    {
        if(!dfn[i])root = i, Tarjan(i);
    }
    int cn = 0;
    for(int i = 1; i <= n; i++)
    {
        if(cut[i])cn++;
    }
    printf("%d\n", cn);
    for(int i =  1; i <= n; i++)
    {
        if(cut[i])printf("%d ", i);
    }
    printf("\n");
}

N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
思想:第一问就是问你最少给多少人发信息,所以利用缩点,然后寻找入度为0的点就是第一问的答案。第二问,最少增加多少条边,这个图示强联通图,所以就是缩点之后,找到入度和初度为0的最大值。

#include<stdio.h>
#include<iostream>
#include<stack>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 10010;
const int N = 500100;
stack<int>s;
int n, m, idx = 0, k = 1, Bcnt = 0, cn = 0, ans;
int head[N], head1[N];
int ins[maxn];
int dfn[maxn], low[maxn];
int belong[maxn], in[maxn], out[maxn];
 
struct edge
{
    int v, next;
} e[N];
struct edge_
{
    int v, next;
} e_[N];
void add(int u, int v)
{
    e[k].v = v;
    e[k].next = head[u];
    head[u] = k++;
}
void add_(int u, int v)
{
    e_[cn].v = v;
    e_[cn].next = head1[u];
    head1[u] = cn++;
}
void tarjan(int u)//强连通分量
{
    int v;
    dfn[u] = low[u] = ++idx;
    ins[u] = 1;
    s.push(u);
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        v = e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if(ins[v])low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        Bcnt++;
        do
        {
            v = s.top();
            s.pop();
            ins[v] = 0;
            belong[v] = Bcnt;
        }
        while(u != v);
    }
}
void init()
{
    idx = 0, k = 1, Bcnt = 0, cn = 1, ans = -1;
    memset(head1, -1,sizeof(head1));
    memset(head, -1, sizeof(head));
 
}
 
int main()
{
    int T, p, u, v, choose;
    init();
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        int v;
        while(scanf("%d", &v) && v != 0)
        {
            add(i, v);
        }
    }
    for(int i= 1; i <= n; i++)if(!dfn[i])tarjan(i);
    for(int i = 1; i <= n; i++)
    {
        int v;
        for(int j = head[i]; j != -1; j = e[j].next)
        {
            v = e[j].v;
            if(belong[i] != belong[v])//缩点
            {
                add_(belong[i], belong[v]);
                in[belong[v]]++;
                out[belong[i]]++;
            }
        }
    }
    int ans1 = 0, ans2 = 0;
    for(int i = 1; i <= Bcnt; i++)
    {
        if(!in[i])ans1++;
        if(!out[i])ans2++;
    }
    printf("%d\n", ans1);
    if(Bcnt ==  1)
    {
        printf("0\n");
        return 0;
    }
 
 
    ans = max(ans1, ans2);
    printf("%d\n", ans);
    return 0;
}

13.Trie:

给你n个数,让你求出任意两个数的异或值的最大值。
思路:本题如果采用异或操作时间指定会超时,所以就需要优化,我们可以换一种方法去思考这个问题,可以把所有的A[i]的写成二进制,把这些二进制用Trie来存储,在查找第A[i]的异或的最大值的时候,我们可以从头开始找的时候如果有跟A[i]的二进制相反的那么就找相反的,如果没有相反的就找相同的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
const int N  = 3e7+10;
int a[maxn];
int trie[N][2];
int tot = 0;
void Insert(int val){//建树
     int rt = 0;
     for(int i = 30; ~i; i--){
        int x = val>>i&1;//取最高位
        if(!trie[rt][x]){
            trie[rt][x] = ++tot;
        }
        rt = trie[rt][x];
     }
}
int query(int val){
    int rt = 0, res = 0;
    for(int i = 30; ~i; i--){
        int x = val >> i&1;
        if(trie[rt][!x]){//如果有相反的
            res += 1 << i;
            rt = trie[rt][!x];
        }
        else rt = trie[rt][x];//没有相反的
    }
    return res;
}
int main(){
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        Insert(a[i]);
    }
    int res = 0;
    for(int i = 1; i <= n; i++){
        res = max(res, query(a[i]));
    }
    printf("%d\n", res);
}

给定N个字符串S1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int trie[maxn][26];
int sum[maxn], tot = 0;
char str[maxn];
void Insert(char *s, int rt)
{
    int len = strlen(s);
    for(int i = 0; i < len; i++)
    {
        int x = s[i] - 'a';
        if(!trie[rt][x])
        {
            trie[rt][x] = ++tot;
        }
        rt = trie[rt][x];

    }
    sum[rt]++;
}
int query(char *s, int rt)
{
    int len = strlen(s);
    int ans = 0;
    for(int i = 0; i < len; i++)
    {
        int x = s[i] - 'a';
        if(!trie[rt][x])return ans;
        if(trie[rt][x]) ans += sum[trie[rt][x]];
        rt = trie[rt][x];
    }
    return ans;
}
int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    while(n--)
    {
        scanf("%s", str);
        Insert(str, 0);
    }
    while(m--)
    {
        scanf("%s", str);
        printf("%d\n",query(str, 0));
    }
    return 0;
}

给定一个树,树上的边都具有权值。
树中一条路径的异或长度被定义为路径上所有边的权值的异或和:
⊕ 为异或符号。
给定上述的具有n个节点的树,你能找到异或长度最大的路径吗?

#include<bits/stdc++.h>
using namespace std;
const int maxn = 101000;
const int N  = 1000000;
int a[maxn];
int trie[N][2];
int tot = 0;
int head[N];
struct Node
{
    int v, w, next;
} e[N];
int k = 1;
void add(int u, int v, int w)
{
    e[k].v = v;
    e[k].w = w;
    e[k].next = head[u];
    head[u] = k++;
}
void Insert(int val)
{
    int rt = 0;
    for(int i = 30; ~i; i--)
    {
        int x = val>>i&1;
        if(!trie[rt][x])
        {
            trie[rt][x] = ++tot;
        }
        rt = trie[rt][x];
    }
}
int query(int val)
{
    int rt = 0, res = 0;
    for(int i = 30; ~i; i--)
    {
        int x = val >> i&1;
        if(trie[rt][!x])
        {
            res += 1 << i;
            rt = trie[rt][!x];
        }
        else rt = trie[rt][x];
    }
    return res;
}
void dfs(int u, int fa, int sum)
{
    a[u] = sum;
    for(int i = head[u]; ~i; i = e[i].next)
    {
        int v = e[i].v;
        if(v != fa)dfs(v, u, sum^e[i].w);
    }
}
int main()
{
    int n;
    scanf("%d", &n);
    memset(head, -1, sizeof(head));
    for(int i = 1; i <= n-1; i++)
    {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    dfs(0, -1, 0);
    int res = 0;
    for(int i = 0; i < n; i++)Insert(a[i]);
    for(int i = 0; i < n; i++)
    {
        res = max(res, query(a[i]));
    }
    printf("%d\n", res);

}

14.字符串HASH:

最大回文子串:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2001000;//注意数组开的范围
typedef unsigned long long ULL;
char s[maxn];
ULL hr[maxn], hl[maxn], p[maxn];
int base = 131;
ULL query(ULL h[], int l, int r)
{
    return h[r] - h[l-1]*p[r-l+1];
}
int main()
{
    int ca = 0;
    while(scanf("%s", s+1) && strcmp(s+1, "END"))
    {
        int n = strlen(s+1);
        n *= 2;
        for(int i =  n; i > 0; i -= 2) //强行在数组中加入新的辅助符号
        {
            s[i] = s[i/2];
            s[i-1] = 'z'+1;
        }
        p[0] = 1;
        for(int i = 1, j = n; i <= n; i++, j--) //字符串哈希
        {
            hl[i] = hl[i-1]*base + (s[i]-'a'+1);
            hr[i] = hr[i-1]*base +(s[j]-'a'+1);
            p[i] = p[i-1]*base;
        }
        int res = 0;
        int l, r;
        for(int i = 1; i <= n; i++)
        {
            l = 0, r = min(i-1, n-i);
            while(l < r)
            {
                int mid = (l+r+1)>>1;
                if(query(hl, i-mid, i-1) != query(hr, n-(i+mid)+1, n-(i+1)+1))r = mid-1;
                else l = mid;
            }
            if(s[i-l] <= 'z')res = max(res, l+1);
            else res = max(res, l);
        }
        printf("Case %d: %d\n", ++ca, res);
    }

    return 0;
}

我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000100;
#define ULL unsigned long long
ULL h[maxn], p[maxn];
char str[maxn];
int main(){
    scanf("%s", str+1);
    int n = strlen(str+1), q;
    scanf("%d", &q);
    p[0] = 1;
    for(int i = 1; i <= n; i++){
        h[i] = h[i-1] * 131+(str[i] - 'a' + 1);
        p[i] = p[i-1] * 131;
    }
    for(int i = 1; i <= q; i++){
        int l_1, r_1, l_2, r_2;
        scanf("%d %d %d %d", &l_1, &r_1, & l_2, &r_2);
        if(h[r_1]-h[l_1-1]*p[r_1-l_1+1] == h[r_2]-h[l_2-1]*p[r_2-l_2+1]){
            printf("Yes\n");
        }
        else {
            printf("No\n");
        }
    }
return 0;
}

详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n0≤k<n) 表示字符串S的后缀 S(k~n-1)。
把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。
额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。
我们的任务就是求出SA与Height这两个数组。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 301000;
typedef unsigned long long ULL;
ULL p[maxn], h[maxn];
char s[maxn];
int vis[maxn], base = 131, n;
ULL get(int l, int r){
    return h[r] - h[l-1]*p[r-l+1];
}
int Binary_Search(int a, int b){
    int l = 0, r = min(n-a+1, n-b+1);
    while(l < r){
        int mid = (l+r+1)>>1;
        if(get(a, a+mid-1) != get(b, b+mid-1))r = mid-1;
        else l = mid;
    }
    return l;
}
bool cmp(int a, int b){
     int l = Binary_Search(a, b);
     int a_val = a+l > n ? INT_MIN : s[a+l];
     int b_val = b+l > n ? INT_MIN : s[b+l];
     return a_val < b_val;
}
int main(){
    scanf("%s", s+1);
    n = strlen(s+1);
    p[0] = 1;
    for(int i = 1; i <= n; i++){
        h[i] = h[i-1]*base+(s[i]-'a'+1);
        p[i] = p[i-1]*base;
        vis[i] = i;
    }
    sort(vis+1, vis+1+n, cmp);
    for(int i = 1; i <= n; i++){
       printf("%d ", vis[i]-1);
    }
    cout<<endl;
    for(int i = 1; i <= n; i++){
        if(i == 1)printf("0");
        else printf(" %d", Binary_Search(vis[i], vis[i-1]));
    }
    cout<<endl;
return 0;
}

15.矩阵快速幂求斐波那契

[ F n + 1 F n F n F n − 1 ] = [ 1 1 1 0 ] n = [ 1 1 1 0 ] [ 1 1 1 0 ] ⎵ n  times  \left[ \begin{array}{cc}{F_{n+1}} &amp; {F_{n}} \\ {F_{n}} &amp; {F_{n-1}}\end{array}\right]=\left[ \begin{array}{cc}{1} &amp; {1} \\ {1} &amp; {0}\end{array}\right]^{n}=\underbrace{\left[ \begin{array}{cc}{1} &amp; {1} \\ {1} &amp; {0}\end{array}\right] \left[ \begin{array}{cc}{1} &amp; {1} \\ {1} &amp; {0}\end{array}\right]}_{n \text { times }} [Fn+1​Fn​​Fn​Fn−1​​]=[11​10​]n=n times  [11​10​][11​10​]​​

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define mod 10000
struct Matrix
{
    long long ma[2][2];
};
Matrix mul(Matrix A,Matrix B)
{
    Matrix C;
    C.ma[0][0]=C.ma[0][1]=C.ma[1][0]=C.ma[1][1]=0;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                C.ma[i][j]=(C.ma[i][j]+A.ma[i][k]*B.ma[k][j])%mod;
            }
        }
    }
    return C;
}
Matrix pow_mod(Matrix A,long long n)
{
    Matrix B;
    B.ma[0][0]=B.ma[1][1]=1;
    B.ma[0][1]=B.ma[1][0]=0;
    while(n)
    {
        if(n&1) B=mul(B,A);
        A=mul(A,A);
        n>>=1;
    }
    return B;
}
int main()
{
    long long n;
    while(~scanf("%lld",&n)&&n!=-1)
    {
        Matrix A;
        A.ma[0][0]=1;A.ma[0][1]=1;
        A.ma[1][0]=1;A.ma[1][1]=0;
        Matrix ans=pow_mod(A,n);
        printf("%lld\n",ans.ma[0][1]);
    }
    return 0;
}

16.Lucas定理:

普通Lucas定理时间复杂度O(logPlogN),并且p为质数

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
typedef  long long LL;
const int mod = 1000003;
LL f[maxn];//开long long
void init() //初始化阶乘
{
    f[0] = 1;
    for(int i = 1; i <= maxn; i++)
    {
        f[i] = (f[i-1]*i)%mod;
    }
}
LL inv(LL a, LL mod) //求逆元
{
    if(a == 1)return 1;
    return inv(mod%a, mod)*(mod - mod/a)%mod;
}
LL Lucas(LL n, LL m, LL p)//Lucas定理
{
    LL ans = 1;
    while(n && m)
    {
        LL a = n % p;
        LL b = m % p;
        if(a < b)return 0;
        ans = ans*f[a]%p*inv(f[b]*f[a-b]%mod, mod)%mod;
        n /= p;
        m /= p;
    }
    return ans;
}
int main()
{
    int T, ca = 0;
    init();
    scanf("%d", &T);
    while(T--)
    {
        LL a, b;
        scanf("%lld %lld", &a, &b);
        printf("Case %d: %lld\n",++ca, Lucas(a, b, mod));
    }
    return 0;
}

扩展Lucas, 时间复杂度PlogP

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=11;
ll p;
ll n,m;
static ll w[MAXN];
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll res=exgcd(b,a%b,x,y),t;
    t=x;
    x=y;
    y=t-a/b*y;
    return res;
}
ll power(ll a,ll b,ll mod)
{
    ll sm;
    for(sm=1; b; b>>=1,a=a*a%mod)if(b&1)
            sm=sm*a%mod;
    return sm;
}

ll fac(ll n,ll pi,ll pk)
{
    if(!n)return 1;
    ll res=1;
    for(register ll i=2; i<=pk; ++i)
        if(i%pi)(res*=i)%=pk;
    res=power(res,n/pk,pk);
    for(register ll i=2; i<=n%pk; ++i)
        if(i%pi)(res*=i)%=pk;
    return res*fac(n/pi,pi,pk)%pk;
}

ll inv(ll n,ll mod)
{
    ll x,y;
    exgcd(n,mod,x,y);
    return (x+=mod)>mod?x-mod:x;
}

ll CRT(ll b,ll mod)
{
    return b*inv(p/mod,mod)%p*(p/mod)%p;
}

ll C(ll n,ll m,ll pi,ll pk)
{
    ll up=fac(n,pi,pk),d1=fac(m,pi,pk),d2=fac(n-m,pi,pk);
    ll k=0;
    for(register ll i=n; i; i/=pi)k+=i/pi;
    for(register ll i=m; i; i/=pi)k-=i/pi;
    for(register ll i=n-m; i; i/=pi)k-=i/pi;
    return up*inv(d1,pk)%pk*inv(d2,pk)%pk*power(pi,k,pk)%pk;
}

ll exlucus(ll n,ll m)
{
    ll res=0,tmp=p,pk;
    static int lim=sqrt(p)+5;
    for(register int i=2; i<=lim; ++i)if(tmp%i==0)
        {
            pk=1;
            while(tmp%i==0)pk*=i,tmp/=i;
            (res+=CRT(C(n,m,i,pk),pk))%=p;
        }
    if(tmp>1)(res+=CRT(C(n,m,tmp,tmp),tmp))%=p;
    return res;
}

int main()
{
    scanf("%lld %lld%d",&n,&m,&p);
    printf("%d\n",exlucus(n,m));
    return 0;
}

YOLOv11涨点改进 | 全网独家下采样改进、细节涨点篇 | TGRS 2025 | 引入PWD参数化小波下采样模块,减少下采样过程中小目标的关键细节丢失,即插即用,助力高效涨点发论文 本文提出了一种改进YOLOv11小目标检测性能的参数化小波下采样(PWD)模块。PWD通过小波变换保留目标细节信息,增强频率域特征表达,有效解决了传统下采样导致的小目标信息丢失问题。该模块采用双分支设计,结合自适应频率选择和空间补偿机制,在红外图像和低光场景中表现出更强的抗干扰能力。实验表明,PWD能显著提升YOLOv11对弱纹理和低对比度目标的检测精度,同时保持计算效率。文章详细介绍了PWD的结构原理、实现代码及在YOLOv11中的集成方法,为小目标检测提供了有效的改进方案。 阅读详情

相关推荐

在 PyCharm 中使用 Copilot

此外,要在 PyCharm 中使用 GitHub Copilot,您必须有效订阅 Copilot。在 PyCharm 中安装 GitHub Copilot 后,您现在可以将其用作项目的扩展。此外,它很容易安装在不同的 IDE 平台上,您还可以为特定编程语言启用 GitHub 插件。此外,按照以下步骤,您可以轻松地在 PyCharm 中安装 GitHub Copilot。此外,在 IDE 上使用 GitHub Copilot 时,您可以使用一些快捷方式。

一览无遗 1万+

20200612AC自动机(加强版)[模板]

题目描述 给你一个文本串 SS 和 nn 个模式串 T_{1…n}T 1…n ,请你分别求出每个模式串 T_iT i在 SS 中出现的次数。 输入格式 第一行包含一个正整数 nn 表示模式串的个数。 接下来 nn 行,第 ii 行包含一个由小写英文字 母构成的字符串 T_iT 。 最后一行包含一个由小写英文字母构成的字符 串 SS。 输出格式 输出包含 nn 行,其中第 ii 行包含一个非负整数 表示T_iT i 在 SS 中出现的次数。 输入样例 5 a bb aa abaa abaaa abaaabaa

liaoxiyan123的博客 299

PET,CT图像融合笔记

PET-CT怎样进行图像融合?图像融合实现CT与PET图像的融合 OpenCV实现(附实验结果)

chenran187906的博客 8408

洛谷P3796

题目链接 题意:有n个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。哪些模式串在文本串T中出现的次数最多。 题解:ac自动机模板加强版,开一个数组单独记录各个字符串出现的次数,找出最多的即可。(数组一定要初始化!!不然会mle!血的教训,找了半天原因,结果是初始化放进函数忘记调用了_'') 1 #include<bits/stdc++.h>...

只能过样例嘤嘤嘤的博客 223

innovus实战攻略 | 用setUsefulSkewMode解锁时序优化的“隐藏技能”!

就像时序优化的“瑞士军刀”,参数虽多,但掌握了分类逻辑后,用起来其实很简单!如果你有更绝的用法,欢迎在评论区分享,大家一起卷起来!简单来说,它就是时序优化的“乾坤大挪移”。通过调整触发器的时钟到达时间,把关键路径的建立时间“借”给非关键路径,从而实现全局时序收敛。别急,这个命令就是你的“时序救星”!(工具最多只能借1.2ns的裕量,防止“借过头”导致新问题!这个参数能帮你兜底!(对关键路径“下死手”,确保优化到位!(设置上限,给工具套上“紧箍咒”!(直接跳过边界,避免优化反噬!(全局管理,防止“借过头”!

weixin_50820814的博客 1354

SecureCRT串口连接保姆级教程:从驱动安装到波特率设置(附常见问题解决)

本文提供SecureCRT串口连接的保姆级教程,涵盖从USB转串口驱动安装、端口识别到波特率等核心参数设置的完整流程。针对连接失败、乱码、键盘输入无响应等高频问题,给出了深度排查与解决方案,帮助网络与嵌入式开发新手快速掌握串口调试技能。

weixin_29281941的博客 432

Dominating Patterns UVA-1449 AC自动机模板题

OJ:https://vjudge.net/problem/UVA-1449 简单翻译: 给定n个模式串和一个文本串T,只包含小写字母,你的任务是找出哪些模式串在T中出现的次数最多,输出对多的次数和出现最多的那个模式串。如果有多个次数相同的模式串,按输入顺序输出多个模式串。 这种多模但是长度比较短的提直接应该就能想到是AC自动机, 该题也就是一个非常明显的模板题,只需要很小的变动,因为...

P19777的博客 297

clock-gating整理

文章目录clock gating 理解1、latch双稳态器件常见锁存器结构SR锁存器D锁存器2、clock gating(clock low-active) latch + AND gating 电路(clock high-active)latch + OR gating 电路 clock gating 理解 1、latch 参考:https://www.cnblogs.com/IClearner/p/6443539.html 双稳态器件 稳定状态为0或1两种 举例:交叉耦合反相器 假设初始Q为1,则可

d_b_的博客 1万+

模板 AC自动机

题目描述 有$N$ 个由小写字母组成的模式串以及一个文本串$T$ 。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串$T$ 中出现的次数最多。 输入输出格式 输入格式: 输入含多组数据。 每组数据的第一行为一个正整数$N$ ,表示共有$N$ 个模式串,$1 \leq N \leq 150$ 。 接下去$N$ 行,每行一个长度小于等于$70$ 的模式...

aoanping0730的博客 179

AI人工智能 Agent:在仓储物流中的应用

AI人工智能 Agent:在仓储物流中的应用 1. 背景介绍 1.1 问题的由来 仓储物流行业是全球经济运行的基石之一,涉及商品的存储、运输、分拣和配送等多个环节。随着电子商务的普及和消费者对物流速度和精确性的要求不断提高,仓储物流

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 859

基于 Qt C++ 的金融行情交易终端

由于完整的商业级源码非常庞大,我将为你提供一个 **结构完整、可直接编译运行的基础框架**,并详细说明各模块的实现思路和扩展方向。double波动 = quote.lastPrice * (rand()%200 - 100) / 10000.0;// 订单缓存(orderId -> Order)

寒寒豆豆软件公司的博客 189

C++字符串基础

字符&字符串字符转换0-127字符串和数字转换数字表示字符数字表示大写字母数字表示小写字母利用字符计算练习字符数组输入输出输入输出数组下标更改位置 字符转换 eg:将字符转换成数字,将数字转换成字符串 #include <iostream> #include <cstdio> using namespace std; int main() { char ...

kyra1997的博客 1089

AI大模型探索之路-实战篇4:深入DB-GPT数据应用开发框架调研

在当今的人工智能时代,大模型技术的迅猛发展为各行各业带来了前所未有的变革。这些大模型,以其强大的语言理解和生成能力,正在逐步成为智能化应用的核心。然而,如何高效地利用这些大模型,构建出满足各种需求的应用,仍然是一个具有挑战性的问题。DB-GPT,作为一个开源的AI原生数据应用开发框架,应运而生,旨在简化大模型应用的开发过程,让构建智能化应用变得触手可及。本文将深入介绍DB-GPT的核心功能、关键特性,并通过实战操作,展示如何利用DB-GPT进行数据应用开发。DB-GPT是一个开源的AI原生数据应用开发框架。

寻道AI,探索AI无限可能! 1万+

谈一下自己收集资料和整理资料的经验

谈一下自己收集资料和整理资料的经验   自己一直以来主要从事开发工作,对于资料收集和整理这方面做得工作不多,如果你觉得下面内容中表现出的技巧和方法比较拙劣望理解。   收集资料技巧 1.   下面是我资料的重要来源,与大家分享一下 1)   ACM数据库,重点关注(分布式系统、分布式计算、FAST) 2)   Microsoft Research与存储相关的研究     http://

xiaoqiangXX的专栏 4864

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

待整理资料

这里写自定义目录标题待整理资料 待整理资料

YaxiongJiang的博客 236

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Jdbc资料整理

很多年不搞Jdbc了,有点生疏。整理一下资料当作复习。

大苯熊-我的书房 781

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

如何有效地进行资料整理?

小M来分享一下【如何在电脑上进行资料整理】 我们都知道,在工作的时候不免会遇到很多的资料需要整理。随着时间的推移,公司的发展,项目的推进,我们会发现,资料越来越多,甚至有时候发现同样的文件会出现在不同的地方,占用很大的空间。 那么,该怎么分类和整理资料,才能够轻松自如的将我们的资料整理得妥妥当当呢? 为了方便大家理解,我们通过一张思维导图做一下清晰的展示和说明: 如何对资料进行分类整理? 首先要认识的是我们的电脑系统,对系统进行大的区间分类: 我们的系统一般建议分五个区间,就是五个盘,如果没

MindMaster2017的博客 2613

及时的归纳整理资料

昨天公司终于给我换了台电脑,跟随我快六年的 HP5410终于退休了,换成了联想的 T420。有了新电脑,自然要拷贝东西,没想到一拷就是一整天。这自然有拷贝经常中断的缘故,有的需要先打包才能正常传送。但更多的是因为自己电脑的资料实在是太乱的缘故。不管是有用的还是没有的资料一大堆,堆积在一起,令人头大。这让我想起了平时的作风,遇到可能会用的东西都会先保存起来,但是当时却是临时地放置,很多甚至直接放...

winsn2008的专栏 247

整理了一天的自己资料

        一直忙到现在,算了,反正页弄不完了...........也累了,该歇歇了

XmagicX的专栏 1023

资料链接整理

资料链接整理 此文档放置资料的链接 posted on 2009-02-14 05:06 guofu 阅读(...) 评论(...) 编辑 收藏 var allowComments=true,cb_blogId=27017,cb_entryId=1390421,...

weixin_30872789的博客 84

资料整理中的计算机汇总方式,资料整理

[拼音]:ziliao zhengli[外文]:data processing对调查得来的大量原始资料进行检查、核实、分类、汇总及编辑的全过程。目的是使资料系统化、条理化,以便进一步深入分析。社会研究所搜集的资料一般可分为数据资料和文字资料。前者是通过结构化的调查问卷及访问表格得来的,它涉及大量调查对象,对此可进行统计分组和汇总;后者多为无结构的观察、访谈材料和文献资料,一般是少数典型或个案的材料...

weixin_30125993的博客 1193

整理一些资料,主观为自己,客观为大家

整理一些资料,主观为自己,客观为大家

没有名字 1098

资料整理的计算机汇总的主要内容,资料整理的主要内容

1.调查资料整理内容1)收集气象水文、地形地貌、地层岩性、地质构造、地震活动、泥石流发生的历史记录、前人调查研究成果、已有勘查资料和泥石流防治工程文件、与泥石流有关的人类工程活动等资料。2)自然地理调查的地形资料、气象资料、水文资料和植被与土壤资料。3)地质调查的地层岩性资料、地质构造资料、地震与新构造运动资料、不良地质体与松散固体物质资料和水文地质资料等。4)冰川泥石流调查的冰雪融水泥石流和冰湖...

weixin_35920143的博客 203
上一篇: 回文子串的最大长度
下一篇: 可达性统计(拓扑排序)
Re:fused
博客等级 码龄9年 474粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值