暑假牛客杭电

暑假牛客杭电

7.19 牛客二

C

签到,注意数据范围

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
int n, m;

int main()
{
    scanf("%d%d",&n,&m);
    if(n % 2 == 1 && m % 2 == 1) puts("NO");
    else puts("YES");
    return 0;
}

D

签到

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;

int T;

bool cmp(int a1, int b1, int a2, int b2)
{
    if(a1 > b1) swap(a1, b1);
    if(a2 > b2) swap(a2, b2);
    //printf("a1=%d b1=%d a2=%d b2=%d\n",a1,b1,a2,b2);
    if(a1 == 2 && b1 == 8) return true;
    else if(a2 == 2 && b2 == 8) return false;
    else if(a1 == b1 && a2 == b2) return a1 > a2;
    else if(a1 == b1) return true;
    else if(a2 == b2) return false;
    else {
        if((a1+b1)%10 == (a2+b2) % 10) return b1 > b2;
        else return (a1+b1)%10 > (a2+b2) % 10;
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int a1, b1, a2, b2;
        scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
        if(a1 > b1) swap(a1, b1);
        if(a2 > b2) swap(a2, b2);
        if(a1 == a2 && b1 == b2) puts("tie");
        else {
            bool flag = cmp(a1,b1,a2,b2);
            if(flag) puts("first");
            else puts("second");
        }
    }
    return 0;
}

F 立体几何

空间集合,求两个球相交部分的体积

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
 
using namespace std;
typedef long long ll;
 
int T;
const double pi = acos(-1);
double x[4], y[4], z[4];
double k1, k2;
 
double dis(double x1, double y1, double z1, double x2, double y2, double z2)
{
    double ans = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
    return ans;
}
 
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        for(int i = 0;i < 4;i++) {
            scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
        }
        scanf("%lf%lf",&k1,&k2);
        double xc1, yc1, zc1, xc2, yc2, zc2, r1, r2;
        xc1 = (k1*k1*x[1]-x[0])/(k1*k1-1);
        yc1 = (k1*k1*y[1]-y[0])/(k1*k1-1);
        zc1 = (k1*k1*z[1]-z[0])/(k1*k1-1);
        xc2 = (k2*k2*x[3]-x[2])/(k2*k2-1);
        yc2 = (k2*k2*y[3]-y[2])/(k2*k2-1);
        zc2 = (k2*k2*z[3]-z[2])/(k2*k2-1);
        r1 = k1 * dis(x[1],y[1],z[1],x[0],y[0],z[0]) / (k1*k1-1);
        r2 = k2 * dis(x[3],y[3],z[3],x[2],y[2],z[2]) / (k2*k2-1);
        double rmax = max(r1,r2), rmin = min(r1,r2);
        double res = 0;
        double d = (dis(xc1,yc1,zc1,xc2,yc2,zc2));
        if(d >= r1 + r2) res = 0;
        else if(d + rmin <= rmax) res = (4.0/3)*pi*rmin*rmin*rmin;
        else {
            double co = (rmax*rmax+d*d-rmin*rmin) / (2.0*d*rmax);
            double h = rmax * (1-co);
            res += (1.0/3)*pi*(3.0*rmax-h)*h*h;
            co = (rmin * rmin + d*d -rmax*rmax) / (2.0*d*rmin);
            h = rmin*(1-co);
            res += (1.0/3)*pi*(3.0*rmin-h)*h*h;
        }
        printf("%.10f\n",res);
    }
    return 0;
}

K 思维+构造

题意:给出单调栈中数列前几个元素的size,求可能的序列。

将b数组补全,数组中元素相邻不能跳跃递增,补全之后进行排序即可,直接进行模拟。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
 
using namespace std;
typedef long long ll;
const int MAXN = 1e6+50;
 
int n, k;
struct node{
    int p, x;
}N[MAXN];
int a[MAXN];
bool vis[MAXN];
int b[MAXN];
vector<node>v;
 
bool cmp(node a, node b)
{
    if(a.x == b.x) return a.p > b.p;
    else return a.x < b.x;
}
 
int main()
{
    scanf("%d%d",&n,&k);
    while(k--)
    {
        int p, x;
        scanf("%d%d",&p,&x);
        v.push_back({p,x});
        b[p] = x;
    }
    int tmp = 0;
    bool flag = true;
    for(int i = 1;i <= n;i++)
    {
        if(!b[i]) {
            b[i] = ++tmp;
            v.push_back({i,tmp});
        }
        else {
            if(b[i] - b[i-1] > 1) {
                flag = false;
                break;
            }
            tmp = b[i];
        }
    }
    sort(v.begin(),v.end(),cmp);
    /*
    for(int i = 0;i < v.size();i++)
    {
        printf("%d %d\n",v[i].p,v[i].x);
    }
    */
    int cnt = 1;
    for(int i = 0;i < v.size();i++)
    {
        a[v[i].p] = cnt;
        cnt++;
    }
    if(!flag) puts("-1");
    else{
        for(int i = 1;i <= n;i++)
        {
            printf("%d ",a[i]);
        }
        printf("\n");
    }
    return 0;
}

🔺 I BFS

BFS搜索等学会了再补

7.20 杭电一

1001

打表找规律签到

大数据本地就没过 wa了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
int T;

ll qpow(ll a, ll n)
{
    ll res = 1;
    while(n)
    {
        if(n & 1) res = res * a;
        n >>= 1;
        a = a * a;
    }
    return res;
}

int main()
{
    scanf("%d",&T);
    //printf("%lld\n",log_2(1e12));
    //printf("%lld\n",qpow(2,log_2(1e12)-1)-1);
    while(T--)
    {
        ll n, res;
        scanf("%lld",&n);
        if(n == 1) res = 0;
        else {
            ll cnt = 0;
            n--;
            while(n)
            {
                n >>= 1;
                cnt++;
            }
            res = qpow(2,cnt-1)-1;
        }
        printf("%lld\n",res);
    }
    return 0;
}

1009 并查集+贪心

排序后从小到大连边,并查集维护连通块个数。

注意边界(最多联通块个数 和 只有一个连通块)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e5+50, M = 5e5+50;
int T;
int n, m, k;
int fa[N];

struct node{
    int a, b, w;
}Node[M];

int find(int x)
{
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}

bool cmp(node &a, node &b)
{
    return a.w < b.w;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        int cnt = n;
        for(int i = 1;i <= n;i++) fa[i] = i;
        for(int i = 0;i < m;i++)
        {
            int a, b, w;
            scanf("%d%d%d",&a,&b,&w);
            Node[i].a = a;
            Node[i].b = b;
            Node[i].w = w;
        }
        sort(Node,Node+m,cmp);
        /*
        for(int i = 0;i < m;i++)
        {
            printf("a = %d b = %d w = %d\n",Node[i].a,Node[i].b,Node[i].w);
        }
        */
        int D = 0;
        bool flag = true;
        if(n == k) D = 0;
        else{
            for(int i = 0;i < m;)
            {
                D = Node[i].w;
                int u = find(Node[i].a), v = find(Node[i].b);
                //printf("%d %d\n",Node[i].a,Node[i].b);
                if(u != v)
                {
                    fa[u] = v;
                    cnt--;
                }
                //printf("cnt = %d\n",cnt);
                i++;
                while(Node[i].w == D && i < m) {
                    int u = find(Node[i].a), v = find(Node[i].b);
                    if(u != v) {
                        fa[u] = v;
                        cnt--;
                    }
                    i++;
                    //printf("cnt = %d\n",cnt);
                }
                if(cnt == k) break;
                else if(cnt < k) {
                    flag = false;
                    break;
                }
            }
        }

        /*
        for(int i = 1;i <= n;i++) printf("%d ",fa[i]);
        printf("\n");
        */
        if(!flag || cnt != k) puts("-1");
        else printf("%d\n",D);
    }
    return 0;
}

1005 素数筛

签到 找规律+素数筛

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long ll;

const int MAXN = 10000010;
int primes[MAXN], cnt, T, n;
bool vis[MAXN];

int main()
{
    scanf("%d",&T);
    for(int i = 2;i <= MAXN;i++)
    {
        if(!vis[i]) primes[cnt++] = i;
        for(int j = 0;primes[j] * i <= MAXN;j++)
        {
            vis[primes[j] * i] = true;
            if(i % primes[j] == 0) break;
        }
    }

    while(T--)
    {
        scanf("%d",&n);
        ll res = 0;
        for(int i = 3;i <= n;i++) {
            if(!vis[i]) res += 2*i;
            else res += i;
        }
        printf("%lld\n",res);
    }
    return 0;
}

1008 单调栈

题意:求列非递减的最大子矩阵面积

若当前元素比其上一行大则记为1,否则为0.(第一行为0)。

利用单调栈(悬线法)答案

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;

using namespace std;
int T, n, m;
const int MAXN = 2e3+50;
int a[MAXN][MAXN], b[MAXN][MAXN], h[MAXN], stk[MAXN];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(stk,0,sizeof(stk));
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
            {
                scanf("%d",&a[i][j]);
                b[i][j] = 0;
                if(i > 1 && a[i][j] >= a[i-1][j])
                {
                    b[i][j] = 1;
                }
            }
        }

        int ans = 0;
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
            {
                if(b[i][j] == 0) h[j] = 1;
                else h[j]++;
            }
            h[m+1] = 0;
            int tot = 0;
            for(int j = 1;j <= m+1;j++)
            {
                while(tot && h[stk[tot]] > h[j])
                {
                    //printf("j = %d stk[tot-1] = %d h[stk[tot]] = %d ans = %d\n",j,stk[tot-1],h[stk[tot]],(j-stk[tot-1]-1)*h[stk[tot]]);
                    ans = max(ans,(j-stk[tot-1]-1)*h[stk[tot]]);
                    tot--;
                }
                stk[++tot] = j;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

/*
2
4 4
2 2 2 2
1 3 1 1
2 4 0 1
3 5 2 1
4 4
3 3 3 3
2 4 2 2
3 5 1 2
4 6 0 3
*/

🔺 1006 字典树维护异或和

题意:给n个数,让最小区间异或和不小于k,若区间长度相等,输出最小的左端点。

思路:对数列做前缀异或,将题面转化为找两个距离最近的数,使得他们的异或值不小于k。
枚举靠右的那个数,同时维护字典树,字典树每个节点保存范围内最靠右的点的位置。根据k来询问对应的log个节点,从而更新答案。
效率: O ( n l o g n ) O(nlogn) O(nlogn)

7.22 杭电二

1001

计算棱长 n-1 正方体中 所有等边三角形, 计算上下底面每一种边长的等边三角形数量,边平行于每个面。

答案 ∑ i = 1 n − 1 8 i 3 \sum^{n-1}_{i=1}8i^3 i=1n18i3

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long ll;
const int MOD = 1e9+7;
int T;
ll n;

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld",&n);
        n = n % MOD;
        ll ans = 2 * n % MOD * n % MOD * (n-1) % MOD * (n-1) % MOD;
        printf("%lld\n",ans);
    }
    return 0;
}

1012

查找字符串中是否含有相应子串

#include <cstdio>
#include <iostream>
using namespace std;
int T; string s,s0="114514" ;
int main()
{
    cin>>T;
    for(int t=1;t<=T;t++)
    {
        cin>>s;
        if(s.find(s0)!=string::npos)	// npos表示没有找到
            cout<<"AAAAAA"<<endl;
        else
            cout<<"Abuchulaile"<<endl;
    }
    return 0;
}

1005

签到,每次可以将序列中当前字母插入序列之首或序列之尾。问字典序最小可能有几种方式。

答案是 2 序 列 开 头 相 同 字 母 数 − 1 2^{序列开头相同字母数-1} 21​,对后面字母只能插在特定位置

#include <cstdio>
#include <iostream>
#define mod 1000000007
using namespace std;
int T,l,maxl; string s; long long ans;
long long pow(int x);
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    cin>>T;
    for(int t=1;t<=T;t++)
    {
        cin>>l>>s; maxl=0;
        for(int i=1;i<l;i++)
        {
            if(s[i]==s[i-1])
                maxl=i;
            else
                break;
        }
        ans=pow(maxl);
        printf("%lld\n",ans);
    }
    return 0;
}
long long pow(int x)
{
    if(x==0) return 1;
    long long k=pow(x/2);
    k=(k*k)%mod;
    if(x&1) k=(k*2)%mod;
    return k;
}

1008 背包DP

解法1

f [ i ] [ j ] f[i][j] f[i][j]​表示第i个科目拿到k分的最短时间,跑一遍01背包。

m x [ i ] [ j ] mx[i][j] mx[i][j]​表示第i个科目花费k天的最大分数。

d p [ i ] [ j ] dp[i][j] dp[i][j]表示i天挂k科的最大分数

初始化 f [ i ] [ j ] = i n f , d p [ i ] [ j ] = − i n f , m x [ i ] [ j ] = 0 f[i][j]=inf, dp[i][j] = -inf, mx[i][j] = 0 f[i][j]=inf,dp[i][j]=inf,mx[i][j]=0

状态转移

f[i][j] = f[i][j-gra]+day 01背包

mx[i][f[i][k]] = k k = 1~100

dp[j][k] = max(dp[j][k],dp[j-l][k]+mx[i][l]) mx[i][l] >= 60

dp[j][k] = max(dp[j][k],dp[j-l][k-1]+mx[i][l]) mx[i][l] < 60

i = 1~n j = t~1 k = 0~p l = 1 − m i n ( f [ i ] [ 100 ] , j ) l = 1-min(f[i][100],j) l=1min(f[i][100],j)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<vector>

using namespace std;
typedef long long ll;
const int M = 505;

struct node{
    int gra, day;
};
map<string, int>mp;
int dp[1009][15], f[110][130], mx[55][1001];
int T, n, m, t, p;
vector<node>v[500];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        char op[20];
        for(int i = 1;i <= n;i++)
        {
            cin >> op;
            mp[op] = i;
        }
        scanf("%d",&m);
        for(int i = 1;i <= m;i++)
        {
            int x, y;
            cin >> op >> x >> y;
            int id = mp[op];
            v[id].push_back(node{x,y});
        }
        scanf("%d%d",&t,&p);

        /*
        for(int i = 1;i <= n;i++)
        {
            for(int j = 0;j < v[i].size();j++)
            {
                printf("i = %d day = %d gra = %d\n",i,v[i][j].day, v[i][j].gra);
            }
        }
        */

        memset(f,0x3f,sizeof(f));
        memset(dp,-0x3f,sizeof(dp));
        memset(mx,0,sizeof(mx));
        for(int i = 1;i <= n;i++)
        {
            f[i][0] = 0;
            for(int j = 0;j < v[i].size();j++)
            {
                for(int k = 120;k >= v[i][j].gra;k--)
                {
                    f[i][k] = min(f[i][k], f[i][k-v[i][j].gra] + v[i][j].day);
                }
            }
            for(int k = 120; k >= 100; k--) f[i][k] = min(f[i][k],f[i][k+1]);
            for(int k = 1;k <= 100;k++) if(f[i][k] <= 500) mx[i][f[i][k]] = max(mx[i][f[i][k]], k);
        }
        /*
        for(int i = 1;i <= n;i++)
        {
            for(int k = 1;k <= 100;k++)
            {
                if(f[i][k] != 1061109567) printf("f[%d][%d] = %d ",i,k,f[i][k]);
            }
            printf("\n");
        }
        */

        dp[0][0] = 0;
        for(int i = 1;i <= n;i++)
        {
            for(int j = t;j >= 1;j--)
            {
                for(int k = p; k > 0; k--) dp[j][k] = dp[j][k-1];
                dp[j][0] = -1e9;
                for(int k = 0;k <= p;k++)
                {
                    for(int l = 1;l <= f[i][100] && l <= j;l++)
                    {
                        if(mx[i][l] >= 60) dp[j][k] = max(dp[j][k], dp[j-l][k] + mx[i][l]);
                        else if(k) dp[j][k] = max(dp[j][k], dp[j-l][k-1] + mx[i][l]);
                    }
                }
            }
            dp[0][0] = -1e9;
        }
        int ans = -1;
        for(int i = 1;i <= t;i++)
        {
            for(int j = 0;j <= p;j++)
            {
                ans = max(ans,dp[i][j]);
            }
        }
        printf("%d\n",ans);
        mp.clear();
        for(int i = 1;i <= n;i++) v[i].clear();
    }
    return 0;
}
解法2 分组背包思想
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N = 55, S = 105, Time = 510, INF = 0x3f3f3f3f;
int g[N][S], f[N][Time][5];
int n, idx, m, t, p;
typedef pair<int, int> P;
unordered_map<string, int> mp;
vector<P> scores[N];
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        mp.clear();
        idx = 0;
        memset(g, 0x3f, sizeof g);
        memset(f, -0x3f, sizeof f);
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scores[i].clear();

        for(int i = 1; i <= n; i++)
        {
            string s;
            cin >> s;
            mp[s] = ++idx;
        }

        scanf("%d", &m);
        for(int i = 1; i <= m; i++)
        {
            string s;
            int score, day;
            cin >> s;
            scanf("%d%d", &score, &day);
            scores[mp[s]].push_back({score, day});
        }
        scanf("%d%d", &t, &p);

        for(int id = 1; id <= n; id++)
        {
            g[id][0] = 0;
            for(int i = 1, sz = scores[id].size(); i <= sz; i++)
            {
                int score = scores[id][i - 1].x, day = scores[id][i - 1].y;
                for(int j = 100; j >= score; j--)
                {
                    g[id][j] = min(g[id][j], g[id][j - score] + day);
                }
            }
        }

        f[0][0][0] = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int j = t; j >= 0; j--)
            {
                for(int k = 0; k <= 100; k++)
                {
                    int day = g[i][k];

                    for(int pp = p; pp >= 0; pp--)
                    {
                        int kk = k < 60 ;
                        if(pp - kk < 0 || j - day < 0) continue;
                        f[i][j][pp] = max(f[i][j][pp], f[i - 1][j - day][pp - kk] + k);
                    }
                }
            }
        }

        int ans = -INF;
        for(int i = 0; i <= t; i++)
            for(int pp = 0; pp <= p; pp++)
            {
                ans = max(ans, f[n][t][pp]);
            }

        if(ans <= -INF / 2) ans = -1;
        printf("%d\n", ans);
    }
}

7.24 牛客三

E

按照每一列找规律

s [ 2 ] = s [ 1 ] 3 s[2] = s[1]^3 s[2]=s[1]3

s [ i + 1 ] = s [ i ] ∗ b a s e 2 − s [ i − 1 ] s[i+1] = s[i] * base ^ 2 - s[i-1] s[i+1]=s[i]base2s[i1]

#include<bits/stdc++.h>

using namespace std;
typedef unsigned long long ll;
struct node{
    ll i, cnt;
};
int T;
ll n;
ll ans[2000000], id;

void solve()
{
    ans[0] = 1;
    for(ll i = 2;i <= 1e6;i++)
    {
        __int128 tmp = i*i*i, lasttmp = i, llasttmp = i;
        ans[++id] = tmp;
        while(true)
        {
            lasttmp = tmp;
            tmp = tmp * i * i - llasttmp;
            if(tmp > 1e18) break;
            ans[++id] = tmp;
            llasttmp = lasttmp;
        }
    }
    sort(ans,ans+1+id);
}

int main()
{
    solve();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%llu",&n);
        ll res = upper_bound(ans,ans+id+1,n)-ans;
        printf("%llu\n",res);
    }
    return 0;
}

//1000000000000000000

J

求边颜色相同的三角形

用总数减去边异色的三角形(考虑顶点)

a n s = ∑ w [ i ] ∗ ( n − 1 − w [ i ] ) ans = \sum w[i] * (n-1-w[i]) ans=w[i](n1w[i])

C n 3 − a n s / 2 C_n^3 - ans / 2 Cn3ans/2

#include <cstdio>
#include <bitset>
using namespace std;
unsigned z1,z2,z3,z4,b,u;
bitset<8010> edge[8010];
unsigned get()
{
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
}
bool read()
{
  while(!u) u=get();
  bool res=u&1;
  u>>=1; return res;
}
void srand(int x)
{
    z1=x;
    z2=(~x)^0x233333333U;
    z3=x^0x1234598766U;
    z4=(~x)+51;
    u = 0;
}
int w[8010]={0}; long long ans=0;
int main()
{
    int n,seed;
    scanf("%d%d",&n,&seed);
    srand(seed);
    for(int i=0;i<n;i++)
        w[i]=0;
    for(int i=0;i<n;i++)
        for(int j=i+1;j<n;j++)
        {
            edge[j][i]=edge[i][j]=read();
            if(edge[i][j]==0)
                ++w[i],++w[j];
        }
    for(int i=0;i<n;i++)
        ans+=w[i]*(n-1-w[i]);
    printf("%lld",(long long)n*(n-1)*(n-2)/6-ans/2);
    return 0;
}

B 最小生成树

题意:2*2的正方形染3个第四个格子自动染色,染每个格子都有本身的花费,求总共最小的花费。

所有格子被染成黑色的充要条件为:

将每一行和每一列作为一个顶点,对应格子的权重为当前行到当前列的连边,求其最小生成树。

原因为:每个各自表示每一行和每一列,需要最少填满n+m个格子才能全部染黑。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
int n, m;
int a, b, c, d, p;
int A[25000001], id;
struct edge{
    int u, v;
    int w;
    bool operator<(const edge &a)
    {
        return w < a.w;
    }
}E[25000001];

int fa[10001];
int find(int x)
{
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}

int main()
{
    scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
    for(int i = 1;i <= n+m;i++) fa[i] = i;
    A[0] = a;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {
            int &x = A[m *(i - 1) + j - 1];
            A[m *(i - 1) + j] = ((ll)x * x * b + x * c + d) % p;
        }
    }
    /*
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {
            printf("%d ",A[m*(i-1)+j]);
        }
        printf("\n");
    }
    */
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {
            E[id++] = {i,j+n,A[m*(i-1)+j]};
        }
    }
    sort(E,E+id);
    ll ans = 0, cnt = 0;
    for(int i = 0;i < id;i++)
    {
        //printf("%d %d %d\n",E[i].u,E[i].v,E[i].w);
        if(cnt >= n+m-1) break;
        int u = find(E[i].u), v = find(E[i].v);
        if(u != v)
        {
            ans += E[i].w;
            fa[u] = v;
            cnt++;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

7.26 牛客四

F 思维

每次可以删掉一条边或者一个子树

运用并查集,先将图删成一棵树再一次去掉子树

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
typedef long long ll;
const int MAXN = 110;
int n, m;
int fa[MAXN], sz[MAXN];
 
int find(int x)
{
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++) fa[i] = i;
    int cnt = n;
    while(m--)
    {
        int u, v;
        scanf("%d%d",&u,&v);
        int x = find(u), y = find(v);
        if(x != y)
        {
            fa[x] = y;
            cnt--;
        }
        else cnt++;
    }
    if(cnt % 2 == 0) puts("Bob");
    else puts("Alice");
    return 0;
}

I 树状数组求逆序对

答案为逆序对数 - 不相邻的从1枚举到n的逆序对

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
typedef long long ll;
const int MAXN = 2e5+50;
int n, m;
int a[MAXN], c[MAXN], b[MAXN];
ll w[MAXN];
 
struct node{
    int num, id;
    bool operator<(const node &a)
    {
        return num < a.num;
    }
}N[MAXN];
 
inline int lowbit(int x)
{
    return x & (-x);
}
 
void change(int i, int k)
{
    while(i <= n)
    {
        c[i] += k;
        i += lowbit(i);
    }
}
 
int sum(int x)
{
    int ret = 0;
    while(x)
    {
        ret += c[x];
        x -= lowbit(x);
    }
    return ret;
}
 
int main()
{
    scanf("%d",&n);
    // 以下求区间逆序对O(nlogn)
    for(int i = 1;i <= n;i++) {
        scanf("%d",&a[i]);
        N[i].id = i;
        N[i].num = a[i];
    }
    sort(N+1,N+n+1);
    for(int i = 1;i <= n;i++) b[N[i].id] = i;
    ll ans = 0;
    for(int i = n;i > 0;i--)
    {
        change(b[i],1);
        ans += sum(b[i] - 1);
    }
    ll res = 0;
    for(int i = 2;i <= n;)
    {
        if(N[i].id < N[i-1].id)
        {
            w[i] = 1;
            i++;
        }
        i++;
    }
    for(int i = 1;i <= n;i++) res += w[i];
    printf("%lld\n",ans-res);
    return 0;
}

C 构造思维

构造最长公共子序列为特定值的情况

注意:序列长度可以无限大,且一个完整的子序列只要有三个字母

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
const int MAXN = 1010;
int a, b, c, n;
char s1[MAXN], s2[MAXN], s3[MAXN], id;
 
int main()
{
    scanf("%d%d%d%d",&a,&b,&c,&n);
    int minn = min(c,min(a,b));
    int i = 0;
    for(;i < minn;i++)
    {
        s1[i] = 'a';
        s2[i] = 'a';
        s3[i] = 'a';
    }
    if(a == minn)
    {
        if(b+c-a > n) {
            puts("NO");
            return 0;
        }
        for(;i < b;i++)
        {
            s1[i] = 'c';
            s2[i] = 'b';
            s3[i] = 'b';
        }
        for(;i < b+c-a;i++)
        {
            s1[i] = 'c';
            s2[i] = 'b';
            s3[i] = 'c';
        }
        for(;i < n;i++)
        {
            s1[i] = 'c';
            s2[i] = 'b';
            s3[i] = 'd';
        }
    }
    else if(b == minn)
    {
        if(c+a-b > n){
            puts("NO");
            return 0;
        }
        for(;i < a;i++)
        {
            s1[i] = 'b';
            s2[i] = 'b';
            s3[i] = 'c';
        }
        for(;i < c+a-b;i++)
        {
            s1[i] = 'c';
            s2[i] = 'b';
            s3[i] = 'c';
        }
        int id = 0;
        for(;i < n;i++)
        {
            s1[i] = 'd';
            s2[i] = 'b';
            s3[i] = 'c';
        }
    }
    else{
        if(a+b-c > n) {
            puts("NO");
            return 0;
        }
        for(;i < a;i++)
        {
            s1[i] = 'b';
            s2[i] = 'b';
            s3[i] = 'c';
        }
        for(;i < a+b-c;i++)
        {
            s1[i] = 'b';
            s2[i] = 'c';
            s3[i] = 'c';
        }
        for(;i < n;i++)
        {
            s1[i] = 'b';
            s2[i] = 'd';
            s3[i] = 'c';
        }
    }
    printf("%s\n",s1);
    printf("%s\n",s2);
    printf("%s\n",s3);
    return 0;
}

J 二分求区间平均值的最大值

求两个数组子区间最大平均值之和

二分查找子区间平均值最大值

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int n, m, x, y;
double a[MAXN], b[MAXN], tmp[MAXN], sa[MAXN], sb[MAXN];

// 二分查找子区间平均值最大值(二分mid,然后前缀和数组进行处理)
bool check(double a[], int len, int limit, double x, double sa[])
{
    for(int i = 1;i <= len;i++) tmp[i] = a[i] - x;
    sa[0] = 0;
    for(int i = 1;i <= len;i++) sa[i] = sa[i-1] + tmp[i];
    double ans = -1e6, res = 1e6;
    for(int i = limit;i <= len;i++)
    {
        res = min(res, sa[i - limit]);
        ans = max(ans, sa[i] - res);
    }
    if(ans >= 0) return true;
    else return false;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&x,&y);
    for(int i = 1;i <= n;i++) scanf("%lf",&a[i]);
    for(int i = 1;i <= m;i++) scanf("%lf",&b[i]);
    double r = 1e9, l = 0;
    double ans = 0;
    while(r - l > 1e-10)
    {
        double mid = (l+r) / 2;
        if(check(a, n, x, mid, sa)) l = mid;
        else r = mid;
    }
    ans += r;
    r = 1e9, l = 0;
    while(r - l > 1e-10)
    {
        double mid = (l+r) / 2;
        if(check(b, m, y, mid, sb)) l = mid;
        else r = mid;
    }
    ans += r;
    printf("%.10f\n",ans);
    return 0;
}

7.27 杭电三

1011

线段树中区间长度最多为k,问n个结点最多有几个区间

深搜,用unordered_map存n个结点的区间个数

偶数 mp[k] = 2*dfs(k / 2) + 1

奇数 mp[k] = dfs(k / 2) + dfs(k / 2 + 1) + 1

深搜+记忆化搜索

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;
typedef unsigned long long ll;
const int MAXN = 1e7+50;
int T;
ll k, n;
unordered_map<ll, ll>mp;

void init(ll n)
{
    mp.clear();
    ll base = 2;
    for(ll i = n;i <= k;i *= 2)
    {
        mp[i] = base-1;
        base *= 2;
    }
}

ll dfs(ll k, ll n)
{
    if(k <= n) return mp[n];
    if(mp[k]) return mp[k];
    if(k % 2 == 0) {
        mp[k] = 2*dfs(k / 2, n) + 1;
        return 2*dfs(k / 2, n) + 1;
    }
    else {
         mp[k] = dfs(k / 2, n) + dfs(k / 2 + 1, n) + 1;
        return dfs(k / 2, n) + dfs(k / 2 + 1, n) + 1;
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%llu%llu",&k,&n);
        init(n);
        ll cnt = dfs(k, n);
        printf("%llu\n",cnt);
    }
    return 0;
}

1007

简单前缀和问题(只有区间查询)

存储每个数前最近的1,维护区间和

r[rr] - r[max(fa[rr], ll)-1]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 1e6+50;
int T, n, q;

struct color{
    int op, r0, g0, b0;
}Color[MAXN];

int fa[MAXN];
int r[MAXN], g[MAXN], b[MAXN];

void merge(int x)
{
    if(Color[x].op == 1) fa[x] = x;
    else {
        fa[x] = fa[x-1];
    }
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        for(int i = 1;i <= n;i++) fa[i] = i;
        for(int i = 1;i <= n;i++)
        {
            int op, x;
            scanf("%d%X",&op,&x);
            Color[i].op = op;
            Color[i].b0 = x % 256;
            Color[i].r0 = x / 256 / 256;
            Color[i].g0 = x / 256 % 256;
            merge(i);
            r[i] = r[i-1] + Color[i].r0;
            b[i] = b[i-1] + Color[i].b0;
            g[i] = g[i-1] + Color[i].g0;
        }

        while(q--)
        {
            int ll, rr;
            scanf("%d%d",&ll,&rr);
            int ans1 = min(255,r[rr] - r[max(fa[rr], ll)-1]);
            int ans2 = min(255,g[rr] - g[max(fa[rr], ll)-1]);
            int ans3 = min(255,b[rr] - b[max(fa[rr], ll)-1]);
            printf("%02X%02X%02X\n",ans1,ans2,ans3);
        }
    }
    return 0;
}

7.29 杭电四

1001

判断+后面是不是0 && 开头必须是0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>

using namespace std;
string s;
int T;

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        bool flag = true;
        cin >> s;
        if(s[0] != '0') {
            flag = false;
        }
        for(int i = 0;i < s.size();i++)
        {
            if(s[i] == '+') {
                if(s[i+1] != '0') {
                    flag = false;
                    break;
                }
            }
        }
        if(flag) puts("YES");
        else puts("NO");
    }
}

1009

输入字符 倒序判断(汉字可能不连通)

#include <cstdio>
#include <string>
#include<iostream>
#include<algorithm>
using namespace std;
int T; char c=1; bool in[110];
struct node{
    int l, r;
    bool operator<(const node &a)
    {
        return l < a.l;
    }
}N[10];
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
    {
        for(int i=1;i<=100;i++)
            in[i]=0;
        for(int i=1;i<=30;i++)
        {
            for(int j=1;j<=100;j++)
            {
                c=getchar();
                while(c!='.'&&c!='#')   c=getchar();
                if(c=='#')  in[j]=1;
            }
        }
        //for(int i = 1;i <= 100;i++) printf("%d%c",in[i],i % 10 == 0 ? '\n' : ' ');
        printf("Case #%d:\n",t);
        int cnt = 0, i;
        for(i=100;i>=1 && cnt < 6;i--)
        {
            if(in[i]==0)
                continue;
            N[cnt].r = i;
            while(in[i]&&i>=1) i--;
            N[cnt].l = i+1;
            cnt++;
        }
        while(in[i] == 0) i--;
        N[cnt].r = i;
        int j = 1;
        while(in[j] == 0) j++;
        N[cnt].l = j;
        cnt++;
        sort(N,N+cnt);
        for(int i = 0;i < cnt;i++)
        {
            printf("%d %d\n",N[i].l, N[i].r);
        }
    }
    return 0;
}

1002

dfs暴搜,以每个节点为根进行dfs,然后求出 w [ i ] [ j ] w[i][j] w[i][j]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 2030;
const int DA = 19560929;
const int mod1 = 1e9+7;
const int mod2 = 1e9+9;
int T, n, c[MAXN], w[MAXN][MAXN], head[MAXN], id, cnt[MAXN];
bool st[MAXN];
ll po1[MAXN], po2[MAXN];

void init()
{
    po1[0] = 1, po2[0] = 1;
    for(int i = 1;i <= 2010;i++)
    {
        po1[i] = po1[i-1] * DA % mod1;
        po2[i] = po2[i-1] * DA % mod2;
    }
}

struct edge{
    int next, to;
}E[MAXN * 4];

inline void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs(int u, int cur)
{
    st[cur] = true;
    for(int i = head[cur];i != -1;i = E[i].next)
    {
        int p = E[i].to;
        if(!st[p])
        {
            if(cnt[c[p]]) w[u][p] = w[u][cur];
            else w[u][p] = w[u][cur] + 1;
            cnt[c[p]]++;
            dfs(u, p);
            cnt[c[p]]--;
        }
    }
}

int main()
{
    init();
    scanf("%d",&T);
    while(T--)
    {
        memset(head,-1,sizeof(head));
        memset(w,0,sizeof(w));
        id = 0;
        scanf("%d",&n);
        for(int i = 2;i <= n;i++)
        {
            int x;
            scanf("%d",&x);
            addedge(i, x);
            addedge(x, i);
        }
        for(int i = 1;i <= n;i++) scanf("%d",&c[i]);
        for(int i = 1;i <= n;i++)
        {
            w[i][i] = 1;
            for(int j = 1;j <= n;j++) st[j] = false;
            for(int j = 1;j <= n;j++) cnt[j] = 0;
            cnt[c[i]] = 1;
            dfs(i,i);
        }
        /*
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                printf("%d ",w[i][j]);
            }
            printf("\n");
        }
        */
        for(int i = 1;i <= n;i++)
        {
            ll res1 = 0, res2 = 0;
            for(int j = 1;j <= n;j++)
            {
                res1 = (res1 + w[i][j] * po1[j-1] % mod1) % mod1;
                res2 = (res2 + w[i][j] * po2[j-1] % mod2) % mod2;
            }
            printf("%lld %lld\n",res1,res2);
        }
    }
    return 0;
}

7.31 牛客五

H 构造

构造横竖斜着的连续三个矩阵中的数不相等

如下构造

1 1 0 0 1 1 0 0
0 0 1 1 0 0 1 1 
1 1 0 0 1 1 0 0
0 0 1 1 0 0 1 1
#include <cstdio>
using namespace std;
int n,m;
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(j%4==1||j%4==2)
                printf("%d",i%2);
            else
                printf("%d",1-i%2);
        }
        printf("\n");
    }
    return 0;
}

K

方法一:单调队列

题意:求满足:区间最大值-最小值>k的不同区间个数

枚举每一个左端点, 寻找最小右端点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 50;
int n, m;
int a[MAXN], q1[MAXN], q2[MAXN];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 0;i < n;i++) scanf("%d",&a[i]);
    while(m--)
    {
        int k;
        scanf("%d",&k);
        ll ans = 0;
        int hh1 = 0, hh2 = 0, tt1 = -1, tt2 = -1;
        q1[++tt1] = 0, q2[++tt2] = 0;
        for(int i = 0, j = 0;i < n;i++)
        {
            while(hh1 <= tt1 && q1[hh1] < i) hh1++;
            while(hh2 <= tt2 && q2[hh2] < i) hh2++;
            while(j < n && a[q1[hh1]] - a[q2[hh2]] <= k)
            {
                j++;
                while(hh1 <= tt1 && a[q1[tt1]] < a[j]) tt1--;
                while(hh2 <= tt2 && a[q2[tt2]] > a[j]) tt2--;
                q1[++tt1] = j; q2[++tt2] = j;
            }
            if(j < n) ans += n-j;
            else break;
        }
        printf("%lld\n",ans);
    }

    return 0;
}
方法二:ST表+双指针
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+5, M = 22;
int n, m;
ll k;
int a[MAXN], maxx[MAXN][M], minn[MAXN][M], Log[MAXN];

void init()
{
    for(int j = 0;j < M;j++)
    {
        for(int i = 1;i + (1 << j) - 1 <= n;i++)
        {
            if(j == 0) {
                maxx[i][j] = a[i];
                minn[i][j] = a[i];
            }
            else {
                maxx[i][j] = max(maxx[i][j - 1], maxx[i + (1 << (j - 1))][j - 1]);
                minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
}

void cal()	// 求Log 否则cmath库中 log运行时间长
{
    Log[1] = 0;
    for(int i = 2;i <= MAXN;i++) Log[i] = Log[i/2]+1;
}

bool check(int l, int r, ll k)
{
    int lo = Log[r-l+1];
    int maxn = max(maxx[l][lo], maxx[r - (1 << lo) + 1][lo]);
    int minx = min(minn[l][lo], minn[r - (1 << lo) + 1][lo]);
    if(maxn - minx > k) return true;
    else return false;
}

int main()
{
    cal();
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
    init();
    while(m--)
    {
        scanf("%lld",&k);
        ll ans = 0;	// 注意每次初始化
        for(int i = 1, j = 1;i <= n;i++)
        {
            while(!check(i, j, k) && j <= n && i <= j) j++;
            if(j <= n) ans = ans+n-j+1;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

B 概率

题意:开每一个箱子的代价为 w i w_i wi,询问一次剩下有几个黑球代价为c,每个盒子都有0.5的概率白 or 黑,且相互独立,问数学期望最小为多少。

不再开箱的条件为剩下球的颜色相同。

r e s = ∑ i = 1 n ( 1 2 ) n − i ( c + ∑ w i ) res = \sum_{i = 1}^{n}(\frac{1}{2})^{n-i}(c+\sum w_i) res=i=1n(21)ni(c+wi)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
 
using namespace std;
const int M = 1e5+50;
double w[M], sum[M];
double c;
double poww[M];
int n;
 
void init()
{
    poww[0] = 1;
    for(int i = 1;i <= 1e5+5;i++) poww[i] = poww[i-1] * 0.5;
    return;
}
 
bool cmp(double a, double b)
{
    return a > b;
}
 
int main()
{
    init();
    scanf("%d%lf",&n,&c);
    for(int i = 1;i <= n;i++) scanf("%lf",&w[i]);
    sort(w+1,w+1+n);
    sum[0] += c;
    for(int i = 1;i <= n;i++) sum[i] = sum[i-1] + w[i];
    double res = 0, las = 1;
    res += sum[0] * poww[n-1];
    for(int i = 1;i <= n-1;i++)
    {
        res += sum[i] * poww[n-i];
       // printf("%.10f\n",res);
    }
    printf("%.10f\n",min(res, sum[n]-c));
    return 0;
}

D 计数DP

  • 在$ [1,i - 1]$区间里找一个公共子序列
  • 在i 这个点 A [ i ] < B [ i ] A[i] < B[i] A[i]<B[i]
  • i i i 后面的可以任意取

计数时: d p [ i ] [ j ] dp[i][j] dp[i][j]表示 a [ 1 − i ] , b [ 1 − j ] a[1-i],b[1-j] a[1i],b[1j]的公共子序列个数(包含空串)

dp[i][j] = dp[i][j-1]+dp[i-1][j];   if(a[i] == b[j])
dp[i][j] = dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1] if(a[i] != b[j]) 不加1是因为本身dp计数就有空串

后面根据组合数,后 n − i n-i ni m − j m-j mj个中任取 i i i​个

KaTeX parse error: Undefined control sequence: \C at position 26: …^{min(n-i,m-j)}\̲C̲_{n-i}^{k}\C^{k…

对于大数的处理,先定义大数的add, sub, mul避免出错

预处理逆元

方法1(快速幂)
int qmi(int a, int b, int mod)
{
    int res = 1;
    while(b)
    {
        if(b & 1) res = (ll)res * a % mod;
        b >>= 1;
        a = (ll)a * a % mod;
    }
    return res;
}

void init()
{
    fact[0] = 1, infact[0] = 1;
    for(int i = 1;i <= 2*5005;i++) {
        fact[i] = mul(fact[i-1], i);
        infact[i] = mul(infact[i-1], qmi(i, MOD-2, MOD));
    }
    return;
}

ll Cal(int n, int m)
{
    if(n == 0 && m == 0) return 1;
    return mul(mul(fact[n], infact[m]), infact[n-m]);
}
方法二(欧几里得)
ll exgcd(ll a, ll b , ll &x , ll &y){
    if(b){
        int r = exgcd(b , a % b , y , x);
        y -= x * (a / b);
        return r;
    }
    else{
        x = 1 , y = 0;
        return a;
    }
}
 
ll inv(ll x){
    ll a , b , c, d;
    a = x , b = mod;
    exgcd(a,b,c,d);
    c %= mod;
    return c < 0 ? mod + c : c;
}
 
ll dp[N][N];
ll F[2 * N];
ll invF[2 * N];
void pre(){
    F[0] = 1;
    for(int i=1; i < 2 * N ; i++){
        F[i] = Mul(F[i-1] , i);
    }
 
    for(int i = 0; i < 2 * N; i++){
        invF[i] = inv(F[i]);
    }
 
}
 
ll C(ll n , ll x){
    if(n == 0 && x == 0)return 1; 
    return F[n] * invF[x] % mod * invF[n - x] % mod;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int M = 5050;
char a[M], b[M];
ll fact[2*M], infact[2*M];
ll dp[M][M];
const int MOD = 1e9+7;

ll add(ll a, ll b)
{
    return a + b >= MOD ? a + b - MOD : a + b;
}

ll sub(ll a, ll b)
{
    return a - b >= 0 ? a - b : a - b + MOD;
}

ll mul(ll a, ll b)
{
    return a * b % MOD;
}

int qmi(int a, int b, int mod)
{
    int res = 1;
    while(b)
    {
        if(b & 1) res = (ll)res * a % mod;
        b >>= 1;
        a = (ll)a * a % mod;
    }
    return res;
}

void init()
{
    fact[0] = 1, infact[0] = 1;
    for(int i = 1;i <= 2*5005;i++) {
        fact[i] = mul(fact[i-1], i);
        infact[i] = mul(infact[i-1], qmi(i, MOD-2, MOD));
    }
    return;
}

ll Cal(int n, int m)
{
    if(n == 0 && m == 0) return 1;
    return mul(mul(fact[n], infact[m]), infact[n-m]);
}

int main()
{
    init();
    cin >> a+1 >> b+1;
    int n = strlen(a+1), m = strlen(b+1);

    dp[0][0] = 1;
    for(int i = 1;i <= n;i++) dp[i][0] = 1;
    for(int i = 1;i <= m;i++) dp[0][i] = 1;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {
            dp[i][j] = sub(add(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]);
            if(a[i] == b[j]) dp[i][j] = add(dp[i][j], dp[i-1][j-1]);
        }
    }

    /*
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++) printf("%d ",dp[i][j]);
        printf("\n");
    }
    */


    ll ans = 0, res = 0;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= m;j++)
        {
            int x = n-i, y = m-j;
            if(a[i] < b[j]) {
                res = mul(dp[i-1][j-1], Cal(x+y, x));
                ans = add(ans, res);
            }
        }
    }

    printf("%lld\n", ans);

    return 0;
}

8.2 牛客六

I

题意:找区间的交为给定区间的并

每个区间必须包括所有区间,所以取所有区间左端点和离它最远区间右端点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
typedef long long ll;
const int MAXN = 1010;
int T, n, m;
int cf[MAXN * 2], cnt[MAXN];
struct node{
    int l, r;
    bool operator<(node &a)
    {
        return l < a.l;
    }
}N[MAXN];
 
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(cf, 0, sizeof(cf));
        memset(cnt, 0, sizeof(cnt));
        for(int i = 0;i < m;i++)
        {
            int l, r;
            scanf("%d%d",&l, &r);
            if(l <= r) {
                cf[r+1]--;
                cf[l]++;
            }
            else {
                cf[r+n+1]--;
                cf[l]++;
            }
        }
        for(int i = 1;i <= 2 * n;i++)
        {
            cnt[i] = cnt[i-1] + cf[i];
        }
        for(int i = 1;i <= n;i++) cnt[i] += cnt[i+n];
 
        /*
        for(int i = 1;i <= n;i++) printf("%d ", cnt[i]);
        printf("\n");
        */
 
 
        int id = 0;
        for(int i = 1;i <= n;)
        {
            while(!cnt[i]) i++;
            if(i > n) break;
            N[id].l = i;
            while(cnt[i]) i++;
            N[id].r = min(n, i-1);
            id++;
        }
 
        /*
        printf("\n");
        for(int i = 0;i < id;i++) printf("%d %d\n", N[i].l, N[i].r);
        printf("\n");
        */
 
 
        sort(N, N+id);
        printf("%d\n", id);
        printf("%d %d\n", N[0].l, N[id-1].r);
 
 
        for(int i = 1;i < id;i++)
        {
            printf("%d %d\n", N[i].l, N[i-1].r);
        }
 
    }
 
    return 0;
}
 
/*
2
7 3
2 4
6 7
7 1
7 2
5 1
7 2
*/

F

答案为 m a x ( m a x ( a i ) , 平 均 数 ) max(max(a_i), 平均数) max(max(ai),)

每次直接模拟,超过时间换一个锅

#include <cstdio>
using namespace std;
int n,m,num;
long long cnt,a[100010],sum,ans=0,maxx;
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        sum+=a[i];
        if(a[i]>maxx) maxx=a[i];
    }
    ans=sum/m;
    if(ans*m<sum) ++ans;
    num=1; cnt=0;
    if(ans<maxx)
        ans=maxx;
    for(int i=1;i<=n;i++)
    {
        if(a[i]+cnt<=ans)
            printf("1 %d %lld %lld\n",num,cnt,cnt+a[i]),cnt+=a[i];
        else
        {
            printf("2 %d %d %lld %d %lld %lld\n",num+1,0,a[i]-(ans-cnt),num,cnt,ans);
            ++num;
            cnt=a[i]-(ans-cnt);
        }
        if(cnt==ans) ++num,cnt=0;
    }
    return 0;
}

8.9 牛客八

D

a + b = a & b + a | b

K

8.10 杭电七

🔺1007(拓扑排序)

题意: f n ( x ) = f ( f n − 1 ( x ) ) f 1 ( x ) = f ( x ) x , f ( x ) ∈ [ 1 , n ] f_n(x) = f(f_{n-1}(x)) \quad f_1(x) = f(x) \qquad x,f(x) ∈[1,n] fn(x)=f(fn1(x))f1(x)=f(x)x,f(x)[1,n]​​​

g ( x ) = lim ⁡ m − > ∞ 1 m ∑ i = 1 m f i ( x ) 对 x ∈ [ 1 , n ] 是 否 相 同 g(x) = \lim_{m -> ∞} \frac{1}{m}\sum^m_{i=1}f_i(x) \qquad 对x ∈[1,n]是否相同 g(x)=limm>m1i=1mfi(x)x[1,n]

转化题意: i i i a [ i ] a[i] a[i]​连边,问每一个环均值是否相等(进入环的那部分不算在内)

思路:拓扑排序搞掉进入环前的一部分,然后判断

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int T, n;
int a[MAXN], d[MAXN];
queue<int>q;

int main()
{
    cin >> T;
    while(T--)
    {
        while(!q.empty()) q.pop();
        memset(d, 0, sizeof(d));
        cin >> n;
        for(int i = 1;i <= n;i++) {
            scanf("%d", &a[i]);
            d[a[i]]++;
        }
        // 处理进入环之前的无关节点
        for(int i = 1;i <= n;i++){
            if(d[i] == 0) q.push(i);
        }
        while(!q.empty())
        {
            int u = q.front();
            q.pop();
            d[a[u]]--;
            if(d[a[u]] == 0) q.push(a[u]);
        }

        bool flag = true;
        ll sum1 = -1, cnt1 = -1;
        for(int i = 1;i <= n;i++)
        {
            if(d[i] == 0) continue;
            ll sum2 = 0, cnt2 = 0;
            for( ; d[i]; i = a[i])
            {
                d[i] = 0;
                sum2 += i;
                cnt2++;
            }
            if(sum1 == -1 && cnt1 == -1) {
                sum1 = sum2;
                cnt1 = cnt2;
            }
            else {
                if(sum1 * cnt2 != sum2 * cnt1) {
                    flag = false;
                    break;
                }
            }
        }
        if(flag) puts("YES");
        else puts("NO");

    }
    return 0;
}

1003

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
typedef long long ll;
const int MAXN = 10010;

int T, k;
double x, y, x_1, y_1, x_2, y_2, h, w;
double poww[MAXN];

void init()
{
    poww[0] = 1.0;
    for(int i = 1;i <= 10001;i++) poww[i] = poww[i-1] * 0.5;
}


int main()
{
    init();
    scanf("%d", &T);
    while(T--)
    {
        cin >> k;
        scanf("%lf%lf%lf%lf%lf%lf", &x, &y, &x_1, &y_1, &x_2, &y_2);
        h = y - y_1;
        w = abs(x_2 - x_1);
        double ans;
        if(k == 2) ans = w * h / 2;
        else {
            ans = (2 * (k - 3) + poww[k] * 6 + 1) * w * h;
        }
        printf("%.3f\n", ans);
    }

    return 0;
}

1010

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        double a, b;
        scanf("%lf%lf", &a, &b);
        if(a <= b) printf("N0 M0R3 BL4CK 1CE TEA!\n");
        else printf("ENJ0Y YOURS3LF!\n");
    }
    return 0;
}

🔺 1005(线性dp)

image-20210810215055631 image-20210810215123258

题解思路正确,递推公式有误
f [ i ] = f [ m i d − 2 ] + f [ l e n − m i d − 1 ] + 1 f [ 1 ] = f [ 2 ] = 0 g [ i ] = g [ i − 2 ] + 1 g [ 1 ] = 0 h [ i ] = 1 n ∑ i = 1 n g ( i − 2 ) + g ( n − i − 1 ) + 1 = 1 + 2 n ∑ i = 1 n − 2 g ( i ) f[i] = f[mid-2]+f[len-mid-1]+1 \\ f[1] = f[2] = 0 \\ g[i] = g[i-2]+1 \\ g[1] = 0 \\ h[i] = \frac{1}{n} \sum_{i=1}^{n}g(i-2)+g(n-i-1)+1 = 1 + \frac{2}{n}\sum_{i=1}^{n-2}g(i) f[i]=f[mid2]+f[lenmid1]+1f[1]=f[2]=0g[i]=g[i2]+1g[1]=0h[i]=n1i=1ng(i2)+g(ni1)+1=1+n2i=1n2g(i)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
const int MAXN = 1e6+50;
int T, n;
int num[MAXN][2], sum[MAXN];

int solve(int len, int type)
{
    if(len <= 0) return 0;
    if(num[len][type]) return num[len][type];
    if(type == 0) {
        return num[len][type] = solve(len - 2, 1) + 1;	// g[i] = g[i-2]+1
    }
    else {
        int mid = (len + 1) / 2;
        return num[len][type] = solve(mid-2, 1) + solve(len-1-mid, 1) + 1;// f[i] = f[mid-2]+f[len-mid-1]+1
    }
}

void init()
{
    for(int i = 1;i <= 1000000;i++) solve(i, 0);
    for(int i = 1;i <= 1000000;i++) sum[i] = (sum[i-1] + num[i][0]) % MOD;
}

int qmi(int a, int b, int mod)
{
    int res = 1;
    while(b)
    {
        if(b & 1) res = (ll) res * a % mod;
        b >>= 1;
        a = (ll)a * a % mod;
    }
    return res;
}

int main()
{
    init();
    cin >> T;
    while(T--)
    {
        cin >> n;
        int ans = (qmi(n, MOD-2, MOD) * (ll)(n + 2*sum[n-2]) % MOD + MOD) % MOD;
        printf("%d\n", ans);
    }
    return 0;
}

🔺 1012(统计数量)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zx9xy4Of-1629254178298)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115024176.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4k4GO1aN-1629254178307)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115113170.png)]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

typedef long long ll;

const int MAXN = 1e5+50;
const int mod = 998244353;
int T, n;
char s[MAXN];
vector<int> a[28];

ll solve(vector<int> vec)
{
    vec.push_back(n + 1);
    ll ans = 0, per = 0, dif = 0;
    for(int i = 1;i < vec.size();i++)
    {
        ans = (ans + per * (vec[i] - vec[i - 1])) % mod;
        dif = (dif + 2 * vec[i - 1] + vec[i] - vec[i - 1]) % mod; // 差分
        per = (per + dif) % mod; // 差分
    }
    return ans;
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%s", s+1);
        n = strlen(s+1);
        for(int i = 0;i < 26;i++) a[i].clear();
        for(int i = 0;i < 26;i++) a[i].push_back(0);
        for(int i = 1;i <= n;i++)
        {
            a[s[i] - 'a'].push_back(i);
        }

        ll ans = 0;
        for(int i = 0;i < 26;i++) ans = (ans + solve(a[i])) % mod;

        printf("%lld\n", ans);
    }
    return 0;
}

🔺1004(生成函数)

对每个箱子分别求其生成函数

奇数

1 : 1 + x 1 + x 2 + ⋯ + = 1 1 − x 1: 1+x^1+x^2+\dots+=\frac{1}{1-x} 1:1+x1+x2++=1x1

3 : 1 + x 2 + x 4 + ⋯ + = 1 1 − x 2 3:1+x^2+x^4+\dots+=\frac{1}{1-x^2} 3:1+x2+x4++=1x21

2 t − 1 : 1 + x t + ⋯ + = 1 1 − x t 2t-1:1+x^t+\dots+=\frac{1}{1-x^t} 2t1:1+xt++=1xt1

偶数:

2 : 1 + x = 1 − x 2 1 − x 2:1+x = \frac{1-x^2}{1-x} 2:1+x=1x1x2

4 : 1 + x + x 2 = 1 − x 3 1 − x 4:1+x+x^2=\frac{1-x^3}{1-x} 4:1+x+x2=1x1x3

2 t : 1 + x + ⋯ + x t = 1 − x t 1 − x 2t:1+x+\dots+x^t=\frac{1-x^t}{1-x} 2t:1+x++xt=1x1xt

生成函数 f ( x ) = 1 − x n + 1 ( 1 − x ) n + 1 = 1 ( 1 − x ) n + 1 − x n + 1 ( 1 − x ) n + 1 f(x)=\frac{1-x^{n+1}}{(1-x)^{n+1}} = \frac{1}{(1-x)^{n+1}}-\frac{x^{n+1}}{(1-x)^{n+1}} f(x)=(1x)n+11xn+1=(1x)n+11(1x)n+1xn+1

x m x^m xm的系数

KaTeX parse error: Undefined control sequence: \C at position 5: ans=\̲C̲_{m+n}^m - \C _…

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 2e6+50;
const int mod = 1e9+7;
ll T, n, m;
ll fact[MAXN], infact[MAXN];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while(b)
    {
        if(b & 1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

void init()
{
    fact[1] = 1, infact[1] = 1;
    for(int i = 2;i <= MAXN - 20;i++)
    {
        fact[i] = fact[i - 1] * i % mod;
        infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
    }
}

ll cal(ll m, ll n)
{
    if(m < n) return 0;	// 注意这个
    ll ans = fact[m] * infact[n] % mod * infact[m - n] % mod;
    return ans;
}

int main()
{
    init();
    cin >> T;
    while(T--)
    {
        cin >> n >> m;
        ll ans = ((cal(m+n, m) - cal(m-1, n)) % mod + mod) % mod;
        printf("%lld\n", ans);
    }
    return 0;
}

8.12 杭电八

1006(素数筛筛约数个数+nim博弈)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e7+50;
int T, n;
bool vis[MAXN];
int primes[MAXN], f[MAXN], a[MAXN], cnt;	// f[i]表示i的质因子个数

void get_primes(int n)
{
    for(int i = 2;i <= n - 20;i++)
    {
        if(!vis[i])
        {
            primes[cnt++] = i;
            f[i] = 1;
        }
        for(int j = 0;(ll)i * primes[j] <= n;j++)
        {
            vis[i * primes[j]] = true;
            f[i * primes[j]] = f[i] + 1;
            if(i % primes[j] == 0) break;
        }
    }
}

int main()
{
    get_primes(MAXN - 20);
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        int ans = 0;
        for(int i = 1;i <= n;i++)
        {
            scanf("%d", &a[i]);
            if(a[i] == 1) a[i] = 0;
            ans ^= f[a[i]];	// nim博弈
        }
        if(ans) puts("Alice");
        else puts("Bob");
    }
    return 0;
}

1003(Prim)

最小生成树中的最大边

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 5050;
int T, n;
bool vis[MAXN];
ll d[MAXN];
struct node{
    ll x, y;
}N[MAXN];

ll prim()
{
    ll res = 0;
    for(int i = 0;i < n;i++)
    {
        int t = -1;
        for(int j = 1;j <= n;j++)
        {
            if(!vis[j] && (t == -1 || d[t] > d[j])) t = j;
        }
        if(i) res = max(res, d[t]);
        vis[t] = true;
        for(int j = 1;j <= n;j++)
            d[j] = min(d[j], (N[j].x - N[t].x) * (N[j].x - N[t].x) + (N[j].y - N[t].y) * (N[j].y - N[t].y));
    }
    return res;
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        memset(vis, false, sizeof(vis));
        memset(d, 0x3f, sizeof(d));
        scanf("%d", &n);
        for(int i = 1;i <= n;i++)
        {
            ll x, y;
            scanf("%lld%lld", &x, &y);
            N[i] = {x, y};
        }
        ll ans = prim();
        printf("%lld\n", ans);
    }
    return 0;
}

8.12 杭电八

1006(素数筛筛约数个数+nim博弈)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e7+50;
int T, n;
bool vis[MAXN];
int primes[MAXN], f[MAXN], a[MAXN], cnt;	// f[i]表示i的质因子个数

void get_primes(int n)
{
    for(int i = 2;i <= n - 20;i++)
    {
        if(!vis[i])
        {
            primes[cnt++] = i;
            f[i] = 1;
        }
        for(int j = 0;(ll)i * primes[j] <= n;j++)
        {
            vis[i * primes[j]] = true;
            f[i * primes[j]] = f[i] + 1;
            if(i % primes[j] == 0) break;
        }
    }
}

int main()
{
    get_primes(MAXN - 20);
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        int ans = 0;
        for(int i = 1;i <= n;i++)
        {
            scanf("%d", &a[i]);
            if(a[i] == 1) a[i] = 0;
            ans ^= f[a[i]];	// nim博弈
        }
        if(ans) puts("Alice");
        else puts("Bob");
    }
    return 0;
}

1003(Prim)

最小生成树中的最大边

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 5050;
int T, n;
bool vis[MAXN];
ll d[MAXN];
struct node{
    ll x, y;
}N[MAXN];

ll prim()
{
    ll res = 0;
    for(int i = 0;i < n;i++)
    {
        int t = -1;
        for(int j = 1;j <= n;j++)
        {
            if(!vis[j] && (t == -1 || d[t] > d[j])) t = j;
        }
        if(i) res = max(res, d[t]);
        vis[t] = true;
        for(int j = 1;j <= n;j++)
            d[j] = min(d[j], (N[j].x - N[t].x) * (N[j].x - N[t].x) + (N[j].y - N[t].y) * (N[j].y - N[t].y));
    }
    return res;
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        memset(vis, false, sizeof(vis));
        memset(d, 0x3f, sizeof(d));
        scanf("%d", &n);
        for(int i = 1;i <= n;i++)
        {
            ll x, y;
            scanf("%lld%lld", &x, &y);
            N[i] = {x, y};
        }
        ll ans = prim();
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值