暑假牛客杭电

暑假牛客杭电

7.17 牛客一

比赛过的(3) 补题(1)
B D F A

签:B (简单推公式)

暑假牛客杭电_第1张图片

2 ∗ r ≥ b 2 * r \ge b 2rb的时候掉下去

若掉不下去

延长梯形随便推推

#include
#include
#include
#include
#include

using namespace std;
double r, a, b, h;

int main()
{
    cin >> r >> a >> b >> h;
    if(2 * r <= b) puts("Drop");
    else {
        puts("Stuck");
        double alhpa = atan((a - b) / (2 * h));
        double h1 = b / (2 * tan(alhpa)), t = r * cos(alhpa), h3 = r * sin(alhpa);
        double h2 = t / tan(alhpa);
        double ans = h3 + h2 - h1;
        printf("%.10f\n", ans);
    }

    return 0;
}

签:D (暴力模拟枚举)

给你一个矩阵,问你有多少个地方有连续的 x 个 0,一定要在一行中。

#include

using namespace std;

int n, m, a[2001][2001], b[2001], ans;

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        int num = 0;
        for (int j = 1; j <= n; j++) {
            scanf("%1d", &a[i][j]);
            if (a[i][j] == 0) num++;
                else num = 0;
            if (num >= m) ans++;
        }
    }

    printf("%d", ans);

    return 0;
}

签:F

题意寻找l到r区间中的一个数x连续子序列 m o d    3 = 0 \mod3=0 mod3=0的个数( 1 ≤ l ≤ r ≤ 1 e 18 1\le l\le r \le1e18 1lr1e18

大于100的都有,小于100的枚举。

#include
#include
#include
#include

using namespace std;
typedef long long ll;
int T, id;
ll l, r;
int ans[105];

void init()
{
    for(int i = 1;i <= 9;i++) if(i % 3 == 0) ans[id++] = i;
    for(int i = 10;i <= 99;i++)
    {
        if(i % 3 == 0 || (i % 10) % 3 == 0 || ((i / 10) % 3 == 0))
            ans[id++] = i;
    }
}

int main()
{
    init();
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lld%lld", &l, &r);
        ll res;
        if(r < 100) {
            res = upper_bound(ans, ans + id, r) - lower_bound(ans, ans + id, l);
        }
        else if(l >= 100) res = r - l + 1;
        else res = (r - 100 + 1) + upper_bound(ans, ans + id, 100) - lower_bound(ans, ans + id, l);
        printf("%lld\n", res);
    }
    return 0;
}

铜:A 博弈论

f [ 0 ] [ 0 ] f[0][0] f[0][0]开始枚举,此时是必败态, f [ i + k ] [ j + s ∗ k ] = 1 f[i + k][j + s * k] = 1 f[i+k][j+sk]=1 f [ i + s ∗ k ] [ j + k ] = 1 f[i + s * k][j + k] = 1 f[i+sk][j+k]=1

枚举之后下一个状态是必胜态

正解需要打表

#include
#include
#include
#include

using namespace std;

int T;
bool f[5001][5001];

void init()
{
    for(int i = 0; i <= 5000; i++)
    {
        for(int j = 0; j <= 5000; j++)
        {
            if(f[i][j] == 0)
            {
                for(int k = 1; k + i <= 5000; k++)
                {
                    for(int s = 0; j + s * k <= 5000; s++)
                    {
                        f[i + k][j + s * k] = 1;
                    }
                }
                for(int k = 1; j + k <= 5000; k++)
                {
                    for(int s = 0; i + s * k <= 5000; s++)
                    {
                        f[i + s * k][j + k] = 1;
                    }
                }
            }
        }
    }
}

int main()
{
    init();

    scanf("%d", &T);
    while(T--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        if(f[n][m] == 1) puts("Alice");
        else puts("Bob");
    }

    return 0;
}

7.19 牛客二

比赛过的(3) 补题(2)
C D F K I

签: C

签到,注意数据范围

#include
#include
#include
#include

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
#include
#include
#include

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
#include
#include
#include
#include
 
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 b b中,每个给出的 b i b_i bi前一定有一段 1 , 2 , … , b i − 1 1, 2, \dots, b_{i - 1} 1,2,,bi1的子序列,不妨把这段子序列连续地放在 b i b_i bi前,之后判断构造出的数组 b b b否合法。

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

数组 b 合法即 b 0 = 1 b_0 = 1 b0=1 且不存在两个相邻的数后者与前者之差大于 1 。

之后,考虑 1 的位置,因为 1 是最小的,每次出现一定会将栈清空且无法被之后的数清空,所以 1 一定在最右的$ b_i = 1$ 处。

接下来考虑 2 的位置,因为此时 2 是剩下未使用过的数中最小的数,所以 2 会在次右的 b i = 1 b_i = 1 bi=1 或最右的 b i = 2 b_i = 2 bi=2处。

……

重复以上步骤直至将 n 个数放完。

即,将构造好的数组 b 按值从小到大,位置从右至左的顺序依次赋上 1 , 2 , … , n 1,2,\dots,n 1,2,,n 即可。

#include
#include
#include
#include
#include
 
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];
vectorv;
 
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

题意:给你一个地图,通过控制两个企鹅从起点((20,20),(20,1))到终点((1,20),(1,1))且两个企鹅镜像移动(及同时上下,左右相反),当一边碰到障碍或边界,是不会移动的,所以可能只有一侧移动,求最少的步数和路径图;

思路:bfs+路径记录
1.创建一个四维数组来记录;
2.两个企鹅只要有一只可以移动就继续进行;
3.在队列中创建一个string来存储路径;

就是一个简单的BFS,使用一个四维数组$v i s [ x 1 ] [ y 1 ] [ x 2 ] [ y 2 ] $[y2]记录当前位置( x 1 , y 1 ) , ( x 2 , y 2 )

的状态,然后按照字典序遍历当前点四周位置,合法则继续走,不合法跳过,走到终点就停止。

#include
#include
#include
#include
#include
#include

using namespace std;

char mp1[45][45], mp2[45][45], ans1[45][45], ans2[45][45];
bool vis[45][45][45][45];
int res;
string s;
struct node{
    int x1, y1, x2, y2, cnt;
    char c;
}p[45][45][45][45];

char rou[4] = {'D', 'L', 'R', 'U'};
int pg1[4][2] = {1, 0, 0, -1, 0, 1, -1, 0};
int pg2[4][2] = {1, 0, 0, 1, 0, -1, -1, 0};
queueq;

bool check1(int x1, int y1)
{
    if(x1 < 1 || x1 > 20 || y1 < 1 || y1 > 20 || mp1[x1][y1] == '#') return false;
    return true;
}

bool check2(int x2, int y2)
{
    if(x2 < 1 || x2 > 20 || y2 < 1 || y2 > 20 || mp2[x2][y2] == '#') return false;
    return true;
}

void bfs()
{
    memset(vis, false, sizeof(vis));
    memset(p, 0x3f, sizeof(p));
    while(!q.empty()) q.pop();
    p[20][20][20][1] = {20, 20, 20, 1, 0};
    q.push({20, 20, 20, 1, 0});
    vis[20][20][20][1] = true;
    while(!q.empty())
    {
        auto u = q.front();
        q.pop();
        //printf("%d %d %d %d %d\n", u.x1, u.y1, u.x2, u.y2, u.cnt);
        int x1 = u.x1, y1 = u.y1, x2 = u.x2, y2 = u.y2, cnt = u.cnt;
        for(int i = 0;i < 4;i++)
        {
            int px1 = x1 + pg1[i][0], py1 = y1 + pg1[i][1], px2 = x2 + pg2[i][0], py2 = y2 + pg2[i][1];
            char c = rou[i];

            // 如果两个都能走
            if(check1(px1, py1) && check2(px2, py2))
            {
                if(p[px1][py1][px2][py2].cnt > cnt + 1 && !vis[px1][py1][px2][py2])
                {
                    p[px1][py1][px2][py2] = {x1, y1, x2, y2, cnt+1, c};
                    q.push({px1, py1, px2, py2, cnt + 1});
                    vis[px1][py1][px2][py2] = true;
                }
            }
            //如果只有企鹅一能走
            else if(check1(px1, py1) && !check2(px2, py2))
            {
                px2 = x2, py2 = y2;
                if(p[px1][py1][px2][py2].cnt > cnt + 1 && !vis[px1][py1][px2][py2])
                {
                    p[px1][py1][px2][py2] = {x1, y1, x2, y2, cnt+1, c};
                    q.push({px1, py1, px2, py2, cnt + 1});
                    vis[px1][py1][px2][py2] = true;
                }
            }
            //如果只有企鹅二能走
            else if(!check1(px1, py1) && check2(px2, py2))
            {
                px1 = x1, py1 = y1;
                if(p[px1][py1][px2][py2].cnt > cnt + 1 && !vis[px1][py1][px2][py2])
                {
                    p[px1][py1][px2][py2] = {x1, y1, x2, y2, cnt+1, c};
                    q.push({px1, py1, px2, py2, cnt + 1});
                    vis[px1][py1][px2][py2] = true;
                }
            }
        }
    }

    res = p[1][20][1][1].cnt;

    // 模拟路径

    int ux1 = 1, uy1 = 20, ux2 = 1, uy2 = 1;
    for(int i = 0;i < 4;i++)
    {
        while(p[ux1][uy1][ux2][uy2].cnt != 0)
        {
            ans1[ux1][uy1] = 'A';
            ans2[ux2][uy2] = 'A';
            auto &t = p[ux1][uy1][ux2][uy2];
            s += t.c;
            ux1 = t.x1, uy1 = t.y1, ux2 = t.x2, uy2 = t.y2;
        }
    }
    ans1[20][20] = 'A', ans2[20][1] = 'A';
    reverse(s.begin(), s.end());

    return;
}

int main()
{
    for(int i = 1;i <= 20;i++)
    {
        cin >> mp1[i] + 1 >> mp2[i] + 1;
    }

    memcpy(ans1, mp1, sizeof(mp1));
    memcpy(ans2, mp2, sizeof(mp2));

    bfs();
    printf("%d\n", res);
    cout << s << endl;

    for(int i = 1;i <= 20;i++)
    {
        cout << ans1[i] + 1 << ' ' << ans2[i] + 1 << endl;
    }
    return 0;
}

7.20 杭电一

比赛过的(4) 补题(0)
1001 1009 1005 1008

签:1001

打表找规律签到

大数据本地就没过 wa了

#include
#include
#include
#include

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
#include
#include
#include

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
#include
#include
#include

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
#include
#include
#include

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,若区间长度相等,输出最小的左端点。

字典树需要开的空间等于节点数,而不是需要插入的数的数量,搞错经常会wa、TLE

因为区间异或和不具有单调性,所以很难找到高效的算法去找到一个区间满足区间异或和不小于 k {k} k且长度最短。区间问题的一个老套路是通过前缀和转化为两个点的问题,而且位运算有点难度的题都会涉及二进制,因为后面需要用到搜索,可以联想到字典树。

原数组转化为前缀和 f {f} f
我们可以枚举区间右端点 r {r} r,然后找到最近的一个下标 l {l} l满足 f [ l ] ⊗ f [ r ] > = k {f[l]\otimes f[r]>=k} f[l]f[r]>=k,那么这个合法区间就是 [ l + 1 , r ] [l+1,r] [l+1,r]

我们可以按 k {k} k的二进制位去找下标 j {j} j
假设 k {k} k的第 i {i} i位是 s [ i ] {s[i]} s[i] f [ r ] {f[r]} f[r]的第 i {i} i位是 b [ i ] {b[i]} b[i],在字典树中当前遍历到的节点是 r o o t {root} root,一个需要访问的值是 i d {id} id

如果 s [ i ] = = 1 {s[i]==1} s[i]==1,那么 i d = b [ i ] ⊗ 1 {id=b[i] \otimes 1} id=b[i]1,如果此时 t i r [ r o o t ] [ i d ⊗ 1 ] ≠ 0 {tir[root][id\otimes 1]\ne0} tir[root][id1]=0,也就是说这个二进制位可以凑出 1 {1} 1,如果沿着节点 t i r [ r o o t ] [ i d ] = = 0 {tir[root][id]==0} tir[root][id]==0继续搜,那么不管怎么搜都凑得到不小于 k {k} k的数

如果 s [ i ] = = 0 s[i]==0 s[i]==0,那么 i d = b [ i ] {id=b[i]} id=b[i]
如果 t i r [ r o o t ] [ i d ] = = 0 {tir[root][id]==0} tir[root][id]==0,也就是说不管 f [ j ] {f[j]} f[j]取哪一个(第 i + 1 ∼ 30 {i+1 \sim 30} i+130位已经确定),这个二进制位都不能凑出 1 {1} 1,就凑不到不小于 k {k} k的数,就没必要接着搜了

搜索完 f [ r ] {f[r]} f[r]后把 f [ r ] {f[r]} f[r]插入到字典树,同时把遍历的点标记,标记的值为 r {r} r,表示最近一次遍历这个节点的数的下标

银:1010 莫队算法

银:1007(推公式+bsgs)

7.22 杭电二

比赛过的(3) 补题(2)
1001 1012 1005 1008 1011

签:1001

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

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

#include
#include
#include
#include

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 
#include 
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"<

签:1005

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

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

#include 
#include 
#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

铜: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
#include
#include
#include
#include
#include
#include

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

struct node{
    int gra, day;
};
mapmp;
int dp[1009][15], f[110][130], mx[55][1001];
int T, n, m, t, p;
vectorv[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
#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 P;
unordered_map mp;
vector

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); } }

铜:1011(2进制数位DP)

要求 C k = max ⁡ ( A i , B j ) C_k = \max{(A_i, B_j)} Ck=max(Ai,Bj) 满足i & j ≥ k \ge k k

∑ C i \sum C_i Ci

思路:

考虑数组D

满足 D k = m a x ( A i × B j ) D_k = m a x ( A_i × B_j ) Dk=max(Ai×Bj) ( i & j = = k )

C j = m a x { D i } , ( i ≥ j ) C_j=max\{D_i\},(i\ge j) Cj=max{Di}(ij)

计算这个只需要从小到大遍历

思考 k = = i k==i k==i的时候, j j j为多少

举例

n-1的二进制位为10110,从大到小遍历所有的下标i

  • i的二进制位10110时,若使得 i & j = = k ,则j可能的二进制位为:10110j的取值不能超过下标)。
  • i的二进制位10101时,则j可能的二进制为:10101
  • i的二进制位10100时,则j可能的二进制为:1010010101
  • i的二进制位10011时,则j可能的二进制为:10011
  • i的二进制位10010时,则j可能的二进制为:100101001110110
  • i的二进制位10001时,则j可能的二进制为:100011001110101
  • i的二进制位10000时,则j可能的二进制为:10000100011001010100100111011010110
  • ……

!!

i10000去找j进行比较时,很明显能发现:

  • j=10011j=10110j=10110在前几个j时已经比较过了,没必要继续比较。

可以发现,如果将i的二进制位中的某一个0变成1得到j,那么 i & j = = i 。

当然,如果变两位也可以使得 i & j = = i ,但是会重复比较(根据例子可以看出来)。

因为 A i A_i Ai B i B_i Bi 都有可能是负数,所以要存minmax

#include
#include
#include
#include

using namespace std;
typedef long long ll;
int T;
const int mod = 998244353, MAXN = 2e6 + 50;
const ll INF = 1e18; // 这个得开足够大
ll a[MAXN], A[MAXN], b[MAXN], B[MAXN], n;

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lld", &n);
        for(int i = 0;i < n;i++) {
            scanf("%lld", &a[i]);
            A[i] = a[i];
        }
        for(int i = 0;i < n;i++) {
            scanf("%lld", &b[i]);
            B[i] = b[i];
        }
        int m = 0;
        while((1 << m) <= n) m++;
        //printf("m = %lld\n", m);


        for(int i = n; i < (1 << m); i++)
        {
            a[i] = b[i] = INF;
            A[i] = B[i] = -INF;
        }


        // i & j == k 时, tmp 与 i 只有一位不同才进行更新
        for(int i = (1 << m) - 1; i >= 0; i--)
        {
            for(int j = 0;j < m;j++)
            {
                int tmp = 1 << j;
                if((tmp & i) == 0)
                {
                    A[i] = max(A[i], A[i ^ tmp]);
                    a[i] = min(a[i], a[i ^ tmp]);
                    B[i] = max(B[i], B[i ^ tmp]);
                    b[i] = min(b[i], b[i ^ tmp]);
                }
            }
        }

        // 逆序求i & j >= k

        ll last = -INF, res = 0;

        for(int i = n - 1; i >= 0; i--)
        {
            ll ans = -INF;
            ans = max(ans, a[i] * b[i]);
            ans = max(ans, A[i] * b[i]);
            ans = max(ans, A[i] * B[i]);
            ans = max(ans, a[i] * B[i]);
            ans = max(ans, last);
            last = ans;
            //printf("ans = %lld\n", ans);
            res = (res + ans + mod) % mod;
        }
        printf("%lld\n", (res % mod + mod) % mod);
    }
    return 0;
}

银:1002(线段树)

银:1004(Trie)

7.24 牛客三

比赛过的(2) 补题(1)
E J B

铜: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

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 
#include 
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

银:B 最小生成树

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

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

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

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

#include
#include
#include
#include

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;
}

铜:F(dfs暴搜)

给定$ n(1\leq n\leq 4) 张 牌 , 每 张 牌 的 点 数 从 1 到 13 , 在 类 似 24 点 的 规 则 下 , 求 能 否 构 造 出 包 含 n 个 变 量 的 表 达 式 , 其 值 为 张牌,每张牌的点数从1 到13,在类似24点的规则下,求能否构造出包含n个变量的表达式,其值为 11324nm(1\leq m\leq 10^9)$。与24点规则不同,计算过程中必须出现分数,且最后结果为整数。

7.26 牛客四

比赛过的(4) 补题(0)
F I C J

F 思维

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

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

#include
#include
#include
#include
 
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
#include
#include
#include
 
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
#include
#include
#include
 
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
#include
#include
#include

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 杭电三

比赛过的(2) 补题(1)
1011 1007 1004

1011

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

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

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

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

深搜+记忆化搜索

#include
#include
#include
#include
#include

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

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
#include
#include
#include

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;
}

1004(博弈论)

题意:平面上有n条直线,Alice会进行n次操作,每次选出k条直线(k=1,2,3,…n),Bob将画一条直线,若与选中的直线有交点则惩罚加一。Alice想让惩罚最大,Bob反之。最后输出每次操作的惩罚值。

两条直线存在公共点当且仅当它们重合或者它们斜率不同,因此Bob的最优策略一定是避开斜率出现次数最多的那些直线。Alice 为了让 Bob 与尽量多的直线相交,最优策略就是最小化斜率出现次数的最大值,所以不断从每种斜率的直线中各选一种即可。

注意:斜率不好直接储存,因为做了除法,可能会出现精度丢失或这除以0的情况。这里可以用pair储存约分后的最简坐标。不需要直接存相同斜率直线的个数,用j来表示Alice取出直线的阶段,f[j]表示j阶段有多少斜率不同的直线。

#include
#include
#include
#include

using namespace std;
typedef pair PII;
const int MAXN = 1e5 + 50;
int T, n;
PII p[MAXN];
int f[MAXN];

int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1;i <= n;i++)
        {
            int x1, x2, y1, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            int dx = x1 - x2, dy = y1 - y2;
            if(dx == 0) dy = 1;
            else if(dy == 0) dx = 1;
            else {
                if(dx < 0) {dx = -dx, dy = -dy;}
                int d = gcd(abs(dx), abs(dy));
                dx /= d, dy /= d;
            }
            p[i].first = dx, p[i].second = dy;
        }
        sort(p + 1, p + n + 1);

        // for(int i = 0;i < n;i++) printf("%d %d\n", p[i].first, p[i].second);

        int i, j;

        for(i = 1; i <= n; i++) f[i] = 0;
        for(i = 1; i <= n; i = j)
        {
            for(j = i; p[i] == p[j] && j <= n; j++);
            for(int k = 1; k <= j - i; k++) f[k]++;
        }

        for(i = j = 1; i <= n; i++)
        {
            while(f[j] == 0) j++;
            f[j]--;
            printf("%d\n", i - j);
        }
    }
    return 0;
}

7.29 杭电四

比赛过的(2) 补题(1)
1001 1009 1002

1001

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

#include
#include
#include
#include
#include

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 
#include 
#include
#include
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
#include
#include
#include

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 牛客五

比赛过的(3) 补题(1)
H K B D

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 
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
#include
#include
#include

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
#include
#include
#include
#include

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
#include
#include
#include
 
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
#include
#include
#include

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 牛客六

比赛过的(2) 补题(0)
I F

I

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

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

#include
#include
#include
#include
 
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 
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

8.3 杭电五

比赛过的(3) 补题(0)
1003 1006 1007

签 1003

#include
#include
#include
#include

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

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        ll n, k;
        scanf("%lld%lld", &n, &k);
        if(n > k + 1) puts("No");
        else puts("Yes");
    }

    return 0;
}

/*
5
1000000000000000000 999999999999999999

*/

签 1006

#include
#include
#include
#include
#include

using namespace std;
const int MAXN = 2e5+50;
typedef long long ll;
int n, T, a[MAXN];
unordered_mapmp;

ll dfs(int n)
{
    if(mp.count(n)) return mp[n];
    //if(n == 1) return 1;
    //if(n == 2) return 2;
    if(n % 3 == 0) {
        mp[n] = 3 * dfs(n / 3) + 1;
        return mp[n];
    }
    else if(n % 3 == 1) {
        mp[n] = 2 * dfs(n / 3) + dfs(n / 3 + 1) + 1;
        return mp[n];
    }
    else {
        mp[n] = dfs(n / 3) + 2 * dfs(n / 3 + 1) + 1;
        return mp[n];
    }
}

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        mp.clear();
        mp[1] = 1, mp[2] = 3;
        scanf("%d", &n);
        for(int i = 1;i <= n;i++) scanf("%d", &a[i]);
        ll ans = dfs(n);
        printf("%lld\n", ans);
    }
    return 0;
}

签 1007

#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;

const ll mod = 1e9 + 7;

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

ll inv(ll x)
{
    return qpow(x, mod - 2);
}

ll pfh(ll n)
{
    long long tmp = n * ((n + 1) % mod) % mod * inv(2) % mod;
    return tmp;
}

ll lfh(ll n)
{
    long long tmp = n * (n + 1) % mod * (2 * n % mod + 1) % mod * inv(6) % mod;
    return tmp;
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        ll n;
        scanf("%lld", &n);
        n = n % mod;
        ll maxn = (pfh(n) * lfh(n)) % mod * n % mod * n % mod;

        ll min1 = pfh(n) * lfh(n) % mod;
        ll res1 = (((pfh(n) + lfh(n)) % mod - 2 + mod) % mod) * ((n + 2) * (n - 1 + mod) % mod * inv(2) % mod) % mod;
        min1 = (min1 + res1) % mod;
        printf("%lld\n", min1);
        printf("%lld\n", maxn);
    }
    return 0;
}

/*
10
345345645635
345345654634
567354756
7857645778
57674556
34546567
34546567
3456547
345675678
34566567

23434556
*/

8.5 杭电六

比赛过的(1) 补题(1)
1001 1005

签 1001

#include
#include
#include
#include

using namespace std;
const int MAXN = 4e7+50;
int T, n;

int primes[MAXN], cnt;
bool vis[MAXN];

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

int main()
{
    scanf("%d", &T);
    get_primes(MAXN - 2);
    while(T--)
    {
        scanf("%d", &n);
        if(n < 0) {
            int pos = upper_bound(primes, primes+cnt, -n) - primes;
            int ans = primes[pos];
            //printf("ans = %d\n", ans);
            int res = -n;
            while(res <= ans)
            {
                if(!vis[res+1]) {
                    printf("%d\n", res * 2 + 2);
                    break;
                }
                else if(!vis[2*res+3]) {
                    printf("%d\n", res * 2 + 3);
                    break;
                }
                res++;
            }
        }
        else if(n == 0) printf("3\n");
        else{
            if(!vis[n]) printf("1\n");
            else if(!vis[2 * n + 1] || !vis[2 * n - 1]) printf("2\n");
            else {
                int pos = upper_bound(primes, primes+cnt, n) - primes;
                int ans = primes[pos];
                //printf("ans = %d\n", ans);
                int res = n;
                while(res <= ans)
                {
                    if(!vis[res+1])
                    {
                        printf("%d\n", res * 2 + 2);
                        break;
                    }
                    else if(!vis[2 * res + 3])
                    {
                        printf("%d\n", res * 2 + 3);
                        break;
                    }
                    res++;
                }
            }
        }
    }
    return 0;
}

铜:1005(构造)

显然 b 1 , b 2 , … , b m b_1,b_2,\dots,b_m b1,b2,,bm m m m个数放在 m m m个不同的集合中,剩下的 n − m n-m nm个数要放到 m m m个集合中位数。

假设 n = 6 , m = 2 , b 1 = 3 , b 2 = 5 n=6,m=2,b_1=3,b_2=5 n=6,m=2,b1=3,b2=5,那么 1 , 2 , … , n 1,2,\dots,n 1,2,,n这些数会被 b b b分成 1 , 2 , 4 , 6 1,2,4,6 1,2,4,6这三段,且任意两段的任意一对可以配对消掉。所以最后剩下的数字一定是在一段之内的。

  • 如果长度最大的段的数字个数不大于其它段的数字个数之和,那么最终要么全部消掉,要么剩下一个,且剩下的这个数可以在任何一段内。如果会剩下,不妨将最后一段的数字剩下一个,此时再把最后一段的数字放到中位数最小的集合中即可满足题意,所以答案为YES。
  • 如果长度最大的段的数字个数大于其它段的数字个数之和,那么最终剩下的所有数字都在最大的这段内。设中位数小于这一段最小值的集合的个数为x,容易发现当且仅当x不小于这一段剩下的数字时有解,否则无解。
#include 

using namespace std;

int main() {
    int T;
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        vector f(n + 2);
        f[n + 1] = 1;
        while (m--) {
            int v;
            cin >> v;
            f[v] = 1;
        }
        vector> size;
        int have = 0;
        int count = 0;
        for (int i = 1; i <= n + 1; i++) {
            if (f[i]) {
                if (have) {
                  size.push_back(make_pair(have, count));
                }
                have = 0;
                count += 1;
            } else {
                have += 1;
            }
        }
        sort(size.begin(), size.end());
        if (size.empty()) {
            cout << "YES\n";
            continue;
        }
        int sum = 0;
        for (auto s: size) {
            sum += s.first;
        }
        if (size.back().first <= sum - size.back().first + size.back().second) {
            cout << "YES\n";
        } else {
            cout << "NO\n";
        }
    }
    return 0;
}

8.7 牛客七

未参加

8.9 牛客八

比赛过的(4) 补题(0)
E D A K

E

#include 
#include 
#include 
 
using namespace std;
 
bool is_prime(int x)
{
    for(int i = 2; i <= x / i; i ++) {
        if(x % i == 0) {
            return false;
        }
    }
    return true;
}
 
int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        int x;
        scanf("%d", &x);
        if(((x % 4 == 0 && x % 100 != 0) || (x % 400 == 0)) && is_prime(x)) puts("yes");
        else puts("no");
    }
    return 0;
}

D

a + b = a & b + a | b

#include 
#include 
#include 
#include 
 
using namespace std;
 
const int N = 100010;
 
int n;
int b[N];
int c[N];
int d[N];
 
bool check(int x1, int y1, int x2, int y2)
{
    if(!x1 && !y1 && !x2 && !y2) return true;
    if(x1 == 1 && !y1 && !x2 && !y2) return true;
    if(x1 == 1 && !y1 && x2 == 1 && !y2) return true;
    if(x1 == 1 && y1 == 1 && x2 == 1 && !y2) return true;
    if(!x1 && !y1 && x2 == 1 && !y2) return true;
    if(x1 == 1 && !y1 && x2 == 1 && y2 == 1) return true;
    if(x1 == 1 && y1 == 1 && x2 == 1 && y2 == 1) return true;
    return false;
}
 
int main()
{
    scanf("%d", &n);
    for(int i = 2; i <= n; i ++) scanf("%d", &b[i]);
    for(int i = 2; i <= n; i ++) scanf("%d", &c[i]);
    for(int i = 2; i <= n; i ++) d[i] = c[i] - b[i];
    for(int i = 2; i <= n; i ++) {
        if(d[i] < 0) {
            printf("0\n");
            return 0;
        }
    }
    for(int i = 0; i < 30; i ++) {
        for(int j = 2; j < n; j ++) {
            if(!check(b[j] >> i & 1, d[j] >> i & 1, b[j + 1] >> i & 1, d[j + 1] >> i & 1)) {
                printf("0\n");
                return 0;
            }
        }
    }
    int cnt = 1;
    for(int i = 0; i < 30; i ++) {
        bool flag = true;
        for(int j = 2; j <= n; j ++) {
            if((b[j] >> i & 1) != 1 || (d[j] >> i & 1) != 0) {
                flag = false;
                break;
            }
        }
        if(flag) cnt *= 2;
    }
    printf("%d\n", cnt);
    return 0;
}

A

#include 
#include 
#include 
#include 
 
using namespace std;
 
typedef long long ll;
 
const int mod = 4933, N = 100010;
 
ll n, m, k, a, l;
 
ll qmi(ll a, ll b, ll k)
{
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % k;
        a = a * a % k;
        b >>= 1;
    }
    return res;
}
 
ll rev(ll x, ll k)
{
    return qmi(x, k - 2, k);
}
 
int main()
{
    scanf("%lld%lld%lld%lld%lld", &n, &m, &k, &a, &l);
    ll res = a % mod;
    ll tmp = 1;
    for(int i = 1; i <= k; i ++) {
        ll x, y, z;
        scanf("%lld%lld%lld", &x, &y, &z);
        if(!x) continue;
        if(y == 0) {
            //printf("%lld\n", a);
            //tmp = 0;
            continue;
        }
        y = z - y;
        if(y == 0) {
            //printf("%lld\n", a);
            tmp = 0;
            continue;
        }
        tmp = (ll)(tmp * (ll)rev(z, mod) % mod * (ll)(y % mod)) % mod;
    }
    printf("%lld\n", (res + tmp) % mod);
    return 0;
}

K

#include 
#include 
#include 
#include 
#include 
 
using namespace std;
 
double pi = acos(-1);
double d, w;
 
int main()
{
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%lf%lf", &d, &w);
        double x1 = min(d, w);
        double x2 = sqrt(w * w + d * d);
        int ans = 0;
        for(int i = 0; i < 5; i ++) {
            int j = floor((pi - x1 * i) / x2);
            if(j < 0) break;
            ans = max(ans, 2 * i + 3 * j);
        }
        for(int j = 0; j < 5; j ++) {
            int i = floor((pi - x2 * j) / x1);
            if(i < 0) break;
            ans = max(ans, 2 * i + 3 * j);
        }
        printf("%d\n", ans + 4);
    }
    return 0;
}

8.10 杭电七

比赛过的(3) 补题(3)
1003 1010 1012 1007 1004 1005

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
#include
#include
#include
#include

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

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
#include
#include
#include
#include

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 
#include 

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
#include
#include
#include

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-GJjChuQH-1629985021698)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115024176.png)]

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

#include
#include
#include
#include
#include

using namespace std;

typedef long long ll;

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

ll solve(vector 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
#include
#include
#include

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 杭电八

比赛过的(2) 补题(0)
1006 1003

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

#include
#include
#include
#include

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
#include
#include
#include

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.13 牛客九

比赛过的(2) 补题(0)
H E

签:H

#include
#include
#include
#include
#include
 
using namespace std;
int n;
vectorv;
 
int main()
{
    scanf("%d", &n);
    while(n)
    {
        int tmmp = n / 3;
        int tmp = n % 3;
        if(tmp == 0) v.push_back(6);
        else if(tmp == 1) v.push_back(2);
        else v.push_back(3);
        n /= 3;
        if(tmp == 0) n--;
        if((tmmp == 1 && tmp == 0) || tmmp == 0) break;
    }
    for(int i = v.size()-1;i >= 0;i--)
    {
        printf("%d", v[i]);
    }
    printf("\n");
    return 0;
}

8.15 牛客十

比赛过的(2) 补题(0)
H F

签:H

#include 
#include 
#include 
#include 
 
using namespace std;
 
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 0; i < (1 << n); i ++) {
        int cnt = 0;
        for(int j = 0; j < n; j ++) {
            if(i >> j & 1) {
                cnt ++;
            }
        }
        if(cnt & 1) printf("1");
        else printf("0");
    }
    puts("");
    return 0;
}

8.17 杭电九

比赛过的(3) 补题(0)
1002 1003 1007

签:1002

#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;

const int N = 100010;

int n, m;
ll k;
int a[N];

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        scanf("%d%d%lld", &n, &m, &k);
        for(int i = 0; i < n * m; i ++) {
            scanf("%d", &a[i]);
        }
        int ans = 0;
        if(k == 1) {
            for(int i = 0; i < m; i ++) ans = max(ans, a[i]);
        }
        else if(k & 1) {
            ans = 2e9;
            for(int i = 0; i < n; i ++) {
                int tmp = 0;
                for(int j = 0; j < m; j ++) {
                    tmp = max(tmp, a[i * m + j]);
                }
                ans = min(ans, tmp);
            }
        }
        else {
            for(int j = 0; j < m; j ++) {
                int tmp = 2e9;
                for(int i = 0; i < n; i ++) {
                    tmp = min(tmp, a[i * m + j]);
                }
                ans = max(ans, tmp);
            }
        }
        printf("%d\n", max(ans, a[0]));
    }
    return 0;
}

签:1003 (双指针+模拟)

#include
#include
#include
#include

using namespace std;
const int MAXN = 5050;
int T, n;

struct aa{
    int a, id;
    bool operator<(const aa &aaa)
    {
        return a > aaa.a;
    }
}A[MAXN];

int b[MAXN];
int best[MAXN], worst[MAXN];

int main()
{
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        for(int i = 1;i <= n;i++) {
            scanf("%d", &A[i].a);
            A[i].id = i;
        }
        for(int i = 1;i <= n;i++) {
            scanf("%d", &b[i]);
        }
        sort(A+1, A+n+1);

        for(int i = 1;i <= n;i++)
        {
            int maxadd = 0, minadd = 0;
            int maxscore = A[i].a + b[1], minscore = A[i].a + b[n];
            best[A[i].id] = 1;
            worst[A[i].id] = n;
            // best
            for(int j = 2, k = n, x = 1; x <= n && j <= k;x++)	// 注意这边遍历1-n
            {
                if(x == i) continue;
                if(A[x].a + b[k] <= maxscore) {
                    k--;
                }
                else {
                    j++;
                    maxadd++;
                }
            }

            //worst
            for(int j = 1, x = n, k = n-1;x >= 1 && j <= k;x--) // 注意这边遍历1-n
            {
                if(x == i) continue;
                if(A[x].a + b[j] > minscore) {
                    j++;
                }
                else {
                    k--;
                    minadd++;
                }
            }

            best[A[i].id] += maxadd;
            worst[A[i].id] -= minadd;
        }

        for(int i = 1;i <= n;i++)
        {
            printf("%d %d\n", best[i], worst[i]);
        }
    }

    return 0;
}

/*
3
4
1 1 1 1
5 4 3 2
4
6 5 4 4
15 10 5 1
4
1 1 1 1
1 1 1 1
*/

1007

题意:

q q q次操作,每次操作有四种情况。

L L L:从左端加入一个元素

R R R:从右端加入一个元素

G G G:删除值为x的数

Q Q Q:查询队列中最中间的元素 ⌈ m + 1 2 ⌉ \lceil {\frac{m+1}{2}} \rceil 2m+1

双向队列的妙题(绝妙!)

用双向队列 q l q l ql q r q r qr 分别来存左侧和右侧添的元素,每进行一次操作,都要重新维护两个队列,保证 q r q r qr 的 队首为 m i d m i d mid

这题我觉得最妙的就是,这样处理下来,将要删除的元素在维护更新的过程中就将其排除出去了

(类似数组的滚动操作)

思路的转换
要求第 m i d m i d mid 个,若保证我一直知道 m i d m i d mid ,便可直接输出,只要维护两个队列中元素个数相等即可

#include
#include
#include
#include
#include
#include

using namespace std;
const int MAXN = 1e7 + 5;
int l, r;
int vis[MAXN];
dequeql, qr;

void up()
{
    while(l > r)
    {
        while(!vis[ql.back()]) ql.pop_back();
        qr.push_front(ql.back()); ql.pop_back();
        l--; r++;
        vis[qr.front()] = 2;
    }
    while(r > l + 1)
    {
        while(!vis[qr.front()]) qr.pop_front();
        ql.push_back(qr.front()); qr.pop_front();
        l++; r--;
        vis[ql.back()] = 1;
    }
}

signed main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int q;
    cin >> q;
    int tot = 0;
    while(q--)
    {
        char op[2];
        cin >> op;
        if(*op == 'L')
        {
            l++;
            vis[++tot] = 1;
            ql.push_front(tot);
            up();
        }
        else if(*op == 'R')
        {
            r++;
            vis[++tot] = 2;
            qr.push_back(tot);
            up();
        }
        else if(*op == 'G')
        {
            int x;
            cin >> x;
            if(vis[x] == 1) l--;
            else r--;
            vis[x] = 0;
            up();
        }
        else
        {
            while(vis[qr.front()] == 0) qr.pop_front();
            cout << qr.front() << endl;
        }
    }
    return 0;
}

银:1010(分类)

8.19 杭电十

比赛过的(0) 补题(1)
0 1003

铜:1003 (找规律)

题意:给n条直线,满足三条直线不共点,任意两条不重合,输出所有可能满足条件的交点个数。

即求 f [ i ] [ j ] f[i][j] f[i][j]表示 i i i条直线能否凑出 j j j个交点。

即是 f [ n ] = ∑ i = 1 n i ∗ ( i − 1 ) + f [ i ] f[n] = \sum_{i=1}^n i*(i-1) + f[i] f[n]=i=1ni(i1)+f[i] f [ i ] f[i] f[i]表示可行的 f [ i ] [ j ] f[i][j] f[i][j]

b i t s e t bitset bitset优化转移 O ( n 4 / w ) O(n^4/w) O(n4/w)

打表发现答案有相当长的一段连续可行后缀,不可行交点不超过31500

时间可以优化到 O ( 31500 ∗ n 2 / w ) O(31500 * n^2/w) O(31500n2/w)

#include
#include
#include
#include
#include

using namespace std;
const int UP = 31500, N = 705;
int T;
bitset f[N];

void init()
{
    f[0][0] = 1;
    for(int i = 1;i < N - 2;i++)
    {
        for(int j = 0;j <= i - 1;j++)
        {
            f[i] |= (f[j] << j * (i - j));
        }
    }
}

void print(int x)
{
    int limit = min(UP - 1, x * (x - 1) / 2);
    for(int i = 0; i <= limit;i++) if(f[x][i]) printf("%d ", i);
    for(int i = limit + 1; i * 2 <= x * (x - 1); i++) printf("%d ", i);
    printf("\n");
}

int main()
{
    init();
    scanf("%d", &T);
    while(T--)
    {
        int n;
        scanf("%d", &n);
        print(n);
    }
    return 0;
}

扩展:bitset的应用场合

Bitset容器是用来存放bit位元素的,每个元素只占1bit位,取值为0或者1,因而比较节约存储空间,bitset提供了多种方法操作位容器,使用前添加头文件即可使用。

(一)创建bitset对象:

bitset<1000> b,b为bitset对象,它能容纳1000个bit位,每个元素初值为0。

注:bitset的大小定义时必须确定,并且定义后也不能修改。

(二)设定元素的值:

(1)下标法,b[i],下标i的取值从0到n-1;

(2)b.set(),对b中全部元素设置为1;

(3)b.reset(),对b中全部元素设置为0;

(4)b.set(pos,值),等价于b[pos]=值;

(5)b.reset(pos),等价于b[pos]=0。

(三)输出元素:

(1)逐个输出,采用下标法:cout<

(2)整体输出,即全部输出:cout<

应用一: 优化boolean multiplication

在做dp的时候,有时候会需要将两个dp矩阵相乘,且矩阵的元素都是bool型。

计算矩阵 A ∗ B = C A*B=C AB=C

C [ i ] [ j ] = 1 C[i][j]=1 C[i][j]=1当且仅当存在 k k k, A [ i ] [ k ] = 1 A[i][k]=1 A[i][k]=1&& B [ k ] [ j ] = 1 B[k][j]=1 B[k][j]=1

直接算需要 O ( n 3 ) O(n^3) O(n3)的时间。可以用bitset 优化常数。 做法如下:

bitset A[N],B[N],C[N];

void Multi(bitset A[],bitset B[],bitset C[])
{
    for (int i=0;i

原理

如果 A [ i ] [ j ] = 1 A[i][j]=1 A[i][j]=1, C [ i ] [ k ] ∣ = A [ i ] [ j ] C[i][k] |= A[i][j] C[i][k]=A[i][j] & B [ j ] [ k ] B[j][k] B[j][k] <-> C [ i ] [ k ] ∣ = B [ j ] [ k ] C[i][k] |= B[j][k] C[i][k]=B[j][k]

相当于把B的第 j j j行拿去和C的第 i i i行做一次或操作。

eg(待补)

http://codeforces.com/contest/781/problem/D

银:1008(KMP+树状数组)

你可能感兴趣的:(c++,算法,数据结构)