绍兴一中集训记

7.4

来到绍兴一中,让我崩溃的是,有一位神仙,喉咙里经常性的会发出“地狱咆哮”,我曾经zjoi的day2与他同一考场,上海的游族杯acm也与他一起,又见到他了,想到地狱咆哮将会陪伴我十多天,这。。。

晚上也没有做暑假作业的心情了,练了练字,颓了一会儿睡了

7.5

5:45日常醒来,神清气爽
还是不想做暑假作业,作业太多也会是我无所适从。。。
有练了练字,下去吃早饭,不错的

来到绍兴一中,建筑挺不错的,跟慈溪中学差不多~~

开题了

T1

没侵犯版权哦,因为我的博客没人看的

一开始看成权值都是整数的了,然后想打无脑20分暴力
因为树上统计方案总数好像有点麻烦,先搞了个dfs序,在顺着搞,只关心自己的直系父亲即可

说太多也没用,反正我没过样例,后来看见权值是实数,心态立马爆炸,这是不是需要一些数学推到了呀,弃了弃了
放一个自己的0分code:

#include <bits/stdc++.h>
#define maxn 1010
#define LL long long
#define qy 1000000007
using namespace std;
struct Edge{
    int to, next;
}edge[maxn << 1];
int num, head[maxn], cnt, id[maxn], a[maxn], b[maxn], rt, fa[maxn], n;
LL ansx, ansy;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y){ edge[++num].to = y, edge[num].next = head[x], head[x] = num; }

LL ksm(int n, int k){
    if (!k) return 1;
    LL sum = ksm(n, k >> 1);
    sum = sum * sum % qy;
    if (k & 1) sum = sum * n % qy;
    return sum;
}

void dfs(int u){
    id[++cnt] = u;
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (v != fa[u]) dfs(v);
    }
}

void dfs1(int k){
    if (k > n) (++ansx) %= qy; else{
        int u = id[k];
        for (int i = k == 1 ? 0 : (a[fa[u]] + 1); i <= b[u]; ++i){
            a[u] = i;
            dfs1(k + 1);
        }
    }
}

int main()
    n = read();
    ansx = 0, ansy = 1;
    for (int i = 1; i <= n; ++i){
        b[i] = read(), fa[i] = read();
        addedge(fa[i], i);
        if (!fa[i]) rt = i;
        (ansy *= b[i]) %= qy;
    }
    dfs(rt);
    dfs1(1);
    printf("%lld\n", (ansx * ksm(ansy, qy - 2) % qy + qy) % qy);
    return 0;
}
T2

马上反应过来,如果把这个讨厌的 X X X拿掉,就是一道经典贪心
( a 1 , b 1 ) (a1,b1) (a1,b1)数对想排在 ( a 2 , b 2 ) (a2,b2) (a2,b2)前面,必须 a 1 ∗ b 2 &gt; a 2 ∗ b 1 a1*b2&gt;a2*b1 a1b2>a2b1,推导可得
所以40pts就拿到手了,直接dfs出10个数属于前面一半的,剩下的属于后面一半,两组中间自己按照贪心结果排个序,在算一下就行了

想到又可以拿两个子任务, a i = 1 ai=1 ai=1的情况,按b数组从小到大排序
b i = 1 bi=1 bi=1的情况,按a数组从大到小排序

60分轻松到手!
Code:

#include <bits/stdc++.h>
#define maxn 100
#define LL long long
using namespace std;
struct node{
    int a, b;
    double c;
}e[maxn], f[maxn], g[maxn], F[maxn], G[maxn];
LL res;
int n, m, cnt2;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

bool cmp1(node x, node y){ return x.b < y.b; }
bool cmp2(node x, node y){ return x.a > y.a; }
bool cmp3(node x, node y){ return x.c > y.c; }

void work(){
    LL ans = 0, sum = 0;
    for (int i = 1; i <= (n >> 1); ++i) ans += e[i].b * (sum += e[i].a);
    sum += m;
    for (int i = (n >> 1) + 1; i <= n; ++i) ans += e[i].b * (sum += e[i].a);
    printf("%lld\n", ans);
}

void doa1(){
    sort(e + 1, e + 1 + n, cmp1);
    work();
}

void dob1(){
    sort(e + 1, e + 1 + n, cmp2);
    work();
}

void dfs(int k, int cnt){
    if (cnt == (n >> 1)){
        for (int i = k; i <= n; ++i) g[cnt2 + i - k + 1] = e[i];
        for (int i = 1; i <= (n >> 1); ++i) F[i] = f[i], G[i] = g[i];
        sort(F + 1, F + 1 + (n >> 1), cmp3);
        sort(G + 1, G + 1 + (n >> 1), cmp3);
        LL ans = 0, sum = 0;
        for (int i = 1; i <= (n >> 1); ++i) ans += F[i].b * (sum += F[i].a);
        sum += m;
        for (int i = 1; i <= (n >> 1); ++i) ans += G[i].b * (sum += G[i].a);
        res = max(res, ans);
        return;
    }
    f[cnt + 1] = e[k];
    dfs(k + 1, cnt + 1);
    if (cnt2 < (n >> 1)){
        g[++cnt2] = e[k];
        dfs(k + 1, cnt);
        --cnt2;
    }
}

void Do(){
    cnt2 = 0;
    dfs(1, 0);
    printf("%lld\n", res);
}

int main(){
    n = read(), m = read();
    int a1 = 1, b1 = 1;
    for (int i = 1; i <= n; ++i) e[i].a = read();
    for (int i = 1; i <= n; ++i) e[i].b = read();
    for (int i = 1; i <= n; ++i){
        e[i].c = 1.0 * e[i].a / e[i].b;
        a1 &= (e[i].a == 1), b1 &= (e[i].b == 1);
    }
    if (a1) doa1(); else
    if (b1) dob1(); else Do();
    return 0;
}

左边的 b z t m i n a m o t o bztminamoto bztminamoto神犇用dfs切掉了,只是linux评测系统把他的clock卡掉了。。。
正解是dp,我会啦
但是我懒得说惹,直接上代码了
Code:

#include <bits/stdc++.h>
#define maxn 55
using namespace std;
struct node{
    int a, b;
}e[maxn];
int sum[maxn], n, m, dp[maxn][maxn][maxn * 10], ans;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

bool cmp(node x, node y){ return x.a * y.b > x.b * y.a; }

int main(){
    n = read(), m = read();
    for (int i = 1; i <= n; ++i) e[i].a = read();
    for (int i = 1; i <= n; ++i) e[i].b = read();
    sort(e + 1, e + 1 + n, cmp);
    for (int i = n; i; --i) sum[i] = sum[i + 1] + e[i].b;
    for (int sumb = n / 2; sumb <= n / 2 * 10; ++sumb){
        memset(dp, 0x80, sizeof(dp));
        dp[0][0][0] = 0;
        for (int i = 0; i <= n; ++i)
        for (int j = 0; j <= min(n / 2, i); ++j)
        for (int k = 0; k <= sumb; ++k)
        if (dp[i][j][k] >= 0){
            if (j < n / 2) dp[i + 1][j + 1][k] = max(dp[i + 1][j + 1][k], dp[i][j][k] + e[i + 1].a * (sum[i + 1] + k));
            if (i - j < n / 2) dp[i + 1][j][k + e[i + 1].b] = max(dp[i + 1][j][k + e[i + 1].b], dp[i][j][k] + e[i + 1].a * (sumb - k));
        }
        ans = max(ans, dp[n][n / 2][sumb] + sumb * m);
    }
    printf("%d\n", ans);
    fclose(stdin); fclose(stdout);
    return 0;
}
T3

一开始n和m看反,导致我一直怀疑这题难易程度
最小生成树秒掉30分
Code:

#include <bits/stdc++.h>
#define maxn 31
#define maxm 32780
using namespace std;
struct Line{
    int x, y, len;
}line[12500010];
char a[maxm][maxn];
int f[maxm], num, n, m;

bool cmp(Line x, Line y){ return x.len < y.len; }
int get(int k){ return f[k] == k ? k : f[k] = get(f[k]); }

int calc(int x, int y){
    int sum = 0;
    for (int i = 1; i <= m; ++i) sum += (a[x][i] != a[y][i]);
    return sum;
}

int main(){
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= n; ++i){
        for (int j = 1; j <= m; ++j){
            char c = getchar();
            for (; c != 'L' && c != 'R'; c = getchar());
            a[i][j] = c;
        }
        for (int j = 1; j < i; ++j){
            ++num;
            line[num].x = j, line[num].y = i, line[num].len = calc(j, i);
        }
    }
    for (int i = 1; i <= n; ++i) f[i] = i;
    sort(line + 1, line + 1 + num, cmp);
    int ans = 0, tot = 0;
    for (int i = 1; i <= num; ++i){
        int sum1 = get(line[i].x), sum2 = get(line[i].y);
        if (sum1 != sum2){
            ans += line[i].len;
            f[sum1] = sum2;
            ++tot;
            if (tot == n - 1) break;
        }
    }
    printf("%d\n", ans);
    return 0;
}

正解,正在理解。。

A n y w a y Anyway Anyway,看到了传说中的ak爷wzp神仙,zjoi全省rk1zyy神仙等等,小激动~~

7.6

日常练字。。新概念课文依然不想背。。继续推迟
今天的早餐比昨天差了那么一点?

开题
我太莽了吧,开题顺序123来的,不过反正5h时间慢慢来

T1

一眼30分暴力,只是认为暴力只能拿30分,然后因为20M疯狂数据范围的威慑,我把范围开到了 3 10 3^{10} 310,其实 3 13 3^{13} 313也是可以的,然后,我就成了全场暴力唯一一个30分的,其他人都45分。。。
Code:

#include <bits/stdc++.h>
#define maxn 600010
#define LL long long
using namespace std;
LL Sum, f[maxn];
int n, m, power[31], power3[31], a[maxn], cnt, num[31], N;

void Dfs(int k, int s){
    if (k > cnt) Sum += a[s]; else{
        Dfs(k + 1, s);
        Dfs(k + 1, s + power[num[k] - 1]);
    }
}

void dfs(int k, int sum, int s){
    if (k > n){
        Sum = 0;
        Dfs(1, s);
        f[sum] = Sum;
        return;
    }
    dfs(k + 1, sum, s);
    dfs(k + 1, sum + power3[k - 1], s + power[k - 1]);
    num[++cnt] = k;
    dfs(k + 1, sum + (power3[k - 1] << 1), s);
    --cnt;
}

int main(){
    scanf("%d %d", &n, &m);
    power[0] = 1;
    for (int i = 1; i <= n; ++i) power[i] = power[i - 1] * 2;
    power3[0] = 1;
    for (int i = 1; i <= n; ++i) power3[i] = power3[i - 1] * 3;
    N = power[n];
    for (int i = 0; i <= N - 1; ++i){
        char c = getchar();
        for (; !isdigit(c); c = getchar());
        a[i] = c ^ 48;
    }
    dfs(1, 0, 0);
    while (m--){
        int sum = 0;
        for (int i = 1; i <= n; ++i){
            char c = getchar();
            for (; c != '1' && c != '0' && c != '?'; c = getchar());
            sum += power3[n - i] * (c == '?' ? 2 : (c == '1' ? 1 : 0));
        }
        printf("%lld\n", f[sum]);
    }
    return 0;
}
T2

题面着实的又臭又长啊,理解了好久,感觉可以15分 k = 1 k=1 k=1情况暴力

先树形dp求出可以达到的 V m a x Vmax Vmax
dfs的过程中顺便弄个dfs序,并且预处理st数组以便后面倍增求lca用

然后在dfs序上暴力dfs枚举连通块, s u m V = V m a x sumV=Vmax sumV=Vmax时再check一下就行了

然后大样例没过,找了好久没找到哪打炸了,放弃了
放一发0分Code:

#include <bits/stdc++.h>
#define maxn 20
#define maxm 20000
#define LL long long
using namespace std;
struct Edge{
    int to, next, len;
}edge[maxn << 1];
int Num, head[maxn], num, cnt, id[maxn], f[maxn][20], fa[maxn], dp[maxn][maxm], w[maxn], v[maxn];
int Vmax, ans, n, m, k, Max, d[maxn], a[maxn], vis[maxn];

inline LL read(){
    LL s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y, int z){ edge[++Num].to = y, edge[Num].len = z, edge[Num].next = head[x], head[x] = Num; }

void dfs(int u){
    id[++cnt] = u;
    f[u][0] = fa[u];
    for (int i = 0; f[u][i]; ++i) f[u][i + 1] = f[f[u][i]][i];
    if (w[u] <= m) dp[u][w[u]] = v[u], Vmax = max(Vmax, v[u]);
    for (int i = head[u]; i; i = edge[i].next){
        int V = edge[i].to;
        if (V != fa[u]){
            fa[V] = u, d[V] = d[u] + edge[i].len;
            dfs(V);
            for (int i = 0; i <= m - w[u]; ++i)
                if (dp[V][i] >= 0) dp[u][i + w[u]] = max(dp[u][i + w[u]], dp[V][i] + v[u]), Vmax = max(Vmax, dp[u][i + w[u]]);
        }
    }
}

int lca(int u, int v){
    if (d[u] < d[v]) swap(u, v);
    for (int i = 10; i >= 0; --i)
        if (d[u] - (1 << i) >= d[v]) u = f[u][i];
    if (u == v) return u;
    for (int i = 10; i >= 0; --i)
        if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
    return f[u][0];
}

void did(){
    int flag = 0;
    for (int x = 1; x <= num; ++x){
        if (flag) break;
        int Flag = 1;
        for (int i = 1; i <= num; ++i)
            if (i != x){
                int Lca = lca(a[i], a[x]), dist = d[a[i]] + d[a[x]] - (d[Lca] << 1);
                if (dist * v[a[i]] > Max){
                    Flag = 0; break;
                }
            }
        if (Flag) flag = 1;
    }
    if (flag) ++ans;
}

void Dfs(int u, int sum, int sumv){
    if (sumv == Vmax) did();
    if (u > n) return;
    if ((vis[fa[id[u]]] || u == 1) && sum + w[id[u]] <= m){
        vis[id[u]] = 1, a[++num] = id[u];
        Dfs(u + 1, sum + w[id[u]], sumv + v[id[u]]);
        vis[id[u]] = 0, --num;
    }
    Dfs(u + 1, sum, sumv);
}

int main(){
    n = read(), m = read(), k = read(), Max = read();
    for (int i = 1; i <= n; ++i) w[i] = read();
    for (int i = 1; i <= n; ++i) v[i] = read();
    for (int i = 1; i < n; ++i){
        int x = read(), y = read(), z = read();
        addedge(x, y, z); addedge(y, x, z);
    }
    Vmax = 0;
    memset(dp, -1, sizeof(dp));
    dfs(1);
    Dfs(1, 0, 0);
    printf("%d\n", ans);
    return 0;
}
T3

不断出锅。。。
人家肝T3的时候zyy好几次跑过来说出锅
这让我庆幸自己选择的123开题顺序。。。

一眼 2 k 2^k 2k部分分,把长度记录下来排个序,用一种类似去重的方法求得答案~~

n=2的部分分打了个小贪心,咕咕了

Code:

#include <bits/stdc++.h>
#define maxn 1000010
using namespace std;
int n, print[maxn], a[maxn], b[maxn], c[maxn], lena, lenb, lenc;

void Do(){
    for (int i = 1; i <= n; ++i){
        char c = getchar();
        for (; !isdigit(c); c = getchar());
        for (; isdigit(c); c = getchar()) ++a[i];
    }
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; ++i)
        print[a[i] <= a[i - 1] ? a[i] = a[i - 1] + 1 : a[i]] = 1;
    int num = 1000000;
    while (!print[num] && num > 1) -- num;
    for (int i = num; i; --i) printf("%d", print[i]);
}

void Do1(){
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar());
    for (; isdigit(ch); ch = getchar()) a[++lena] = ch ^ 48;
    for (; !isdigit(ch); ch = getchar());
    for (; isdigit(ch); ch = getchar()) b[++lenb] = ch ^ 48;
    if (lena < lenb){
        for (int i = 1; i <= lena; ++i) c[i] = a[i];
        lenc = lena;
        lena = lenb;
        for (int i = 1; i <= lena; ++i) a[i] = b[i];
        lenb = lenc;
        for (int i = 1; i <= lenb; ++i) b[i] = c[i];
    } else
    if (lena == lenb){
        int flag = 1;
        for (int i = 1; i <= lena; ++i)
            if (a[i] < b[i]){
                flag = 0; break;
            } else
            if (a[i] > b[i]) break;
        if (!flag){
            for (int i = 1; i <= lena; ++i) c[i] = a[i];
            for (int i = 1; i <= lena; ++i) a[i] = b[i];
            for (int i = 1; i <= lenb; ++i) b[i] = c[i];
        }
    }
    for (int i = lena; i >= lena - lenb + 1; --i) b[i] = b[i - lena + lenb];
    for (int i = lena - lenb; i; --i) b[i] = 0;
    int tmp = 0;
    for (int i = 1; i <= lenb; ++i)
        if (a[i] == 1 && b[i] == 1){
            a[tmp] = 1;
            for (int j = tmp + 1; j <= lena; ++j) a[j] = 0;
            break;
        } else
        if (!a[i] && !b[i]) tmp = i;
    for (int i = a[0] ? 0 : 1; i <= lena; ++i) printf("%d", a[i] | b[i]);
}

int main(){
    scanf("%d", &n);
    if (n != 2) Do(); else Do1();
    return 0;
}

zyy讲题姿势风骚,气场强大,我却是全程懵逼。。。
知识思维双重难度,这让我一个小菜鸡怎么办呀

7.7

今天放假,没什么好写哒
上午颓OI+暑假作业+雀魂+三国杀
好久不玩三国杀,玩了个司马懿内奸迷迷糊糊的赢了?
下午颓OI+暑假作业+雀魂+三国杀
甄姬+司马懿的组合还是很爽的(要是把司马懿换成张角就更好了)
晚上颓OI+BBC+红楼梦+雀魂+三国杀

睡觉~~

7.8

今天爽飞,原地爆炸
算法+思维双重难度,恭喜自己喜提倒数第二的好成绩~~

T1

20分暴力显然,后面觉得是dp,但是想不出来
Code:

#include <bits/stdc++.h>
#define maxn 3010
#define qy 998244353
using namespace std;
char s[maxn][maxn], a[maxn];
int len[maxn], l[maxn], r[maxn], n, ans;

void check(){
	int cnt = 0;
	for (int i = 1; i <= n; ++i)
		for (int j = l[i]; j <= r[i]; ++j) a[++cnt] = s[i][j];
	for (int i = 1; i <= (cnt >> 1); ++i)
		if (a[i] != a[cnt + 1 - i]) return;
	(++ans) %= qy;
}

void dfs(int k){
	if (k > n) check(); else{
		for (int i = 1; i <= len[k]; ++i)
		for (int j = i; j <= len[k]; ++j){
			l[k] = i, r[k] = j;
			dfs(k + 1);
		}
	}
}

int main(){
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i){
		scanf("%s", s[i] + 1);
		len[i] = strlen(s[i] + 1);
	}
	dfs(1);
	printf("%d\n", ans);
	return 0;
}
T2

8分暴力显然,然而后面的就不会了
瑟瑟发抖
Code:

#include <bits/stdc++.h>
#define LL long long
#define ull unsigned long long
using namespace std;
LL n, m;
ull ans;
bool prime[25000000];

int main(){
	freopen("math.in", "r", stdin);
	freopen("math.out", "w", stdout);
	scanf("%lld%lld", &n, &m);
	if (n > m) swap(n, m);
	if (n == 1){
		ans = sqrt(m);
		printf("%lld\n", ans);
	} else{
		for (int i = 1; i <= m; ++i) prime[i * i] = 1;
		for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) ans += prime[i * j];
		printf("%lld\n", ans);
	}
	return 0;
}
T3

知道是凸包,但是我不会
只能上网在线学习了一下,找到的一个叫做Andrew的算法,照着他打了一遍10分的暴力,但是wa掉了
仍然找不出错误
Code:

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int ans[maxn], n, m;
class Point{
    public:
        double x,y;
        Point (double x = 0,double y = 0):x(x),y(y) {}
        Point operator + (Point a){ return Point(x + a.x, y + a.y); }
        Point operator - (Point a){ return Point(x - a.x, y - a.y); }
        bool operator < (const Point &a) const{ return x == a.x ? y < a.y : x < a.x; }
};
typedef vector<Point> Polygom;

double cross(Point x, Point y){ return x.x * y.y - x.y * y.x; }

bool isclock(Point p0, Point p1, Point p2){
	Point a = p1 - p0, b = p2 - p0;
	return cross(a, b) < 0;
}

double GetArea(Polygom a){
	double ans = 0;
	for (int i = 2; i < a.size(); ++i){
		Point x = a[i - 1] - a[0], y = a[i] - a[0];
		ans += abs(cross(x, y));
	}
	return ans;
}

Polygom AndrewScan(Polygom s){
	Polygom u, l;
	if (s.size() < 3) return s;
	sort(s.begin(), s.end());
	u.push_back(s[0]); u.push_back(s[1]);
	l.push_back(s[s.size() - 1]); l.push_back(s[s.size() - 2]);
	for (int i = 2; i < s.size(); ++i){
		for (int n = u.size(); n >= 2 && !isclock(u[n - 2], u[n - 1], s[i]); --n) u.pop_back();
		u.push_back(s[i]);
	}
	for (int i = s.size() - 3; i >= 0; --i){
		for (int n = l.size(); n >= 2 && !isclock(l[n - 2], l[n - 1], s[i]); --n) l.pop_back();
		l.push_back(s[i]);
	}
	for (int i = 1; i < l.size() - 1; ++i) u.push_back(l[i]);
	return u;
}

int main(){
	freopen("temple.in", "r", stdin);
	freopen("temple.out", "w", stdout);
	scanf("%d%d", &n, &m);
	Polygom a;
	if (m == 1){
		Point q[maxn];
		for (int i = 1; i <= n; ++i) scanf("%lf%lf", &q[i].x, &q[i].y);
		int l, r;
		scanf("%d%d", &l, &r);
		for (int i = l; i <= r; ++i) a.push_back(q[i]);
		a = AndrewScan(a);
		printf("%d\n", (int)GetArea(a));
	} else{
		Point q;
		for (int i = 1; i <= n; ++i){
			scanf("%lf%lf", &q.x, &q.y);
			a.push_back(q);
			a = AndrewScan(a);
			ans[i] = (int)GetArea(a);
		}
		while (m--){
			int l, r;
			scanf("%d%d", &l, &r);
			printf("%d\n", ans[r]);
		}
	}
    return 0;
}

然后晚上我找出了原因,longlong的缘故?
反正我用Graham打好以后,改成longlong就能过10分,但这个我用的是double呀,精度误差?
放一下10分code:

#include <bits/stdc++.h>
#define maxn 100010
#define LL long long
using namespace std;

struct Point{
	LL x, y;
	void read(){ scanf("%lld%lld", &x, &y); }
	Point operator - (const Point &a) const{ return (Point) { x - a.x, y - a.y }; }
	LL operator * (const Point &a) const { return x * a.y - y * a.x; }
}p[maxn], q[maxn], GP;
int n, m, num;

LL cross(Point a, Point b, Point c){ return (b - a) * (c - a); }

LL dis(Point a, Point b){ return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); }

bool cmp(Point a, Point b){
	LL tmp = cross(GP, a, b);
	return tmp != 0 ? tmp > 0 : dis(GP, a) < dis(GP, b);
}

LL Graham(){
	GP = q[1];
	for (int i = 2; i <= num; ++i)
		if (q[i].y < GP.y || (q[i].y == GP.y) && (q[i].x < GP.x)) GP = q[i];
	sort(q + 1, q + 1 + num, cmp);
	int top = 2;
	for (int i = 3; i <= num; ++i){
		while (top >= 2 && cross(q[top - 1], q[top], q[i]) <= 0) --top;
		q[++top] = q[i];
	}
	LL ans = 0;
	for (int i = 2; i < top; ++i) ans += abs(cross(q[1], q[i], q[i + 1]));
	return ans;
}

int main(){
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) p[i].read();
	while (m--){
		int l, r;
		scanf("%d%d", &l, &r);
		num = 0;
		for (int i = l; i <= r; ++i) q[++num] = p[i];
		printf("%lld\n", Graham());
	}
	return 0;
}

完全不会,唉,太菜了

7.9

今天的暴力分又变多了~~
总算没垫底==|
30+25+0=55

T1

不会打暴力,后来会惹
直接 2 6 5 26^5 265枚举结果,计算与初始串的编辑距离
因为常数有点大,顺手加个小剪枝,拿到30分
本来担心计算编辑距离的部分会wa掉,结果告诉我做法是正确的

Code:

#include <bits/stdc++.h>
#define maxn 12000000
using namespace std;
int flag[100], a[100], print[100][100], n, cnt;
char s[100];

int get(int *a, char *s, int x, int y){
    int sum = 0;
    for (int i = 1; i <= n; ++i){
        if (x + i - 1 > n || y + i - 1 > n) break;
        if (a[x + i - 1] == s[y + i - 1]) ++sum;
    }
    return min(n - x + 1, n - y + 1) - sum;
}

void DO(){
    int sum = 100;
    for (int i = 1; i <= 3; ++i){
        sum = min(sum, (i - 1) * 2 + get(a, s, i, 1));
        sum = min(sum, (i - 1) * 2 + get(a, s, 1, i));
    }
    if (sum && sum <= 5 && !flag[sum]){
        flag[sum] = 1;
        for (int i = 1; i <= n; ++i) print[sum][i] = a[i];
        ++cnt;
    }
}

void dfs(int k){
    if (cnt == n) return;
    if (k > n) DO(); else{
        for (int i = 97; i <= 122; ++i){
            a[k] = i;
            dfs(k + 1);
        }
    }
}

int main(){
    scanf("%s", s + 1);
    n = strlen(s + 1);
    dfs(1);
    for (int i = 1; i <= n; ++i, puts("")) for (int j = 1; j <= n; ++j) printf("%c", print[i][j]);
    return 0;
}

Update: Captain ZJ zyy神仙tql,讲的太快没听懂嘤嘤嘤
但是看了ORZ b z t m i n a m o t o bztminamoto bztminamoto的博客后懂了

这是一道很不错的dp题
采用逐位确定的方法

对于每一个询问,逐位填字母,枚举该位字母
对于每个字母,也就是确定了当前的一个前缀,设它是合法的
那么求出对于这个确定的前缀最终可能的与初始串最大/小编辑距离
得到一个范围
那么答案一定在这个范围中间
编辑距离用dp求得
如何构造出最大/小?

注意到n<=25,必定有一个字母在初始串没出现过,字母是谁并不重要,所以设它为27号字母
后面都填这个字母必能使编辑距离最大
后面照着初始串复制一份必能使编辑距离最小

感性理解得知答案必在最小最大范围中间

Code:

#include <bits/stdc++.h>
#define maxn 100
using namespace std;
char s[maxn], ans[maxn];
int n, dp[maxn][maxn];

int dis(){
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 0; i <= n; ++i) dp[i][0] = dp[0][i] = i;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]) + 1, dp[i - 1][j - 1] + (ans[j] != s[i]));
	return dp[n][n];
}

int main(){
	scanf("%s", s + 1);
	n = strlen(s + 1);
	for (int len = 1; len <= n; ++len){
		for (int i = 1; i <= n; ++i)
		for (int c = 'a'; c <= 'z'; ++c){
			ans[i] = c;
			for (int j = i + 1; j <= n; ++j) ans[j] = 'z' + 1;
			if (dis() < len) continue;
			for (int j = i + 1; j <= n; ++j) ans[j] = s[j];
			if (dis() > len) continue;
			break;
		}
		printf("%s\n", ans + 1);
	}
	return 0;
}
T2

10分完全背包暴力显然
突发奇想打个表找找规律,还真找到了。。。
发现对于确定的m,变的x
dp[x]在一段长度为m的区间值是相同的
总是在i为m的倍数时改变,这其实都能发现
然后那个改变的delta正好是 d p [ i / m ] dp[i/m] dp[i/m]

25分的正确解法好像是一个叫做旋转体积背包的东西
我:啥玩意儿?找规律不好吗

Code:

#include <bits/stdc++.h>
#define maxn 20000010
#define qy 1000000007
using namespace std;
int m, x, dp[maxn];

int main(){
    scanf("%d%d", &m, &x);
    dp[1] = 1;
    for (int i = 2; i <= x; ++i){
        dp[i] = dp[i - 1];
        if (i % m == 0) (dp[i] += dp[i / m]) %= qy;
    }
    printf("%d\n", dp[x]);
    return 0;
}
T3

期望题?放弃?
感觉10分可以打

好吧,不能打
只会 n = 1 或 2 n=1或2 n=12的情况
打好去骗分,是期望得个5分的
结果data真不给面子, n &lt; = 3 n&lt;=3 n<=3结果n都是3,这还怎么玩呀

Code:

#include <bits/stdc++.h>
#define qy 1000000007
#define LL long long
using namespace std;
int n, a[100][100];
LL p[100], s, ans;

/*int get(int k){ return k == f[k] ? k : f[k] = get(f[k]); }

void dfs(int u, int cnt, int P){
    if (check()) (ans += (cnt) * P % qy) %= qy;
    if (cnt > 10) return;
    int x = rand()
}*/

int main(){
    freopen("island.in", "r", stdin);
    freopen("island.out", "w", stdout);
    //srand(time(0));
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%lld", &p[i]);/*, f[i] = i;*/
    for (int i = 1; i <= n; ++i){
        scanf("%d", &a[i][0]);
        for (int j = 1; j <= a[i][0]; ++j) scanf("%d", &a[i][j]);
    }
    /*for (int i = 1; i <= n; ++i){
        a[0] = read();
        for (int j = 1; j <= a[0]; ++j) a[j] = read();
    }
    dfs(1, 0, 1);
    printf("%d\n", ans);*/
    if (n == 1) printf("%d\n", 1); else{
        s = 1000000008 - p[1];
        for (int i = 1; i <= 10000000; ++i){
            ans = (ans + (s * ((i & 1) ? p[2] : p[1])) % qy) % qy;
            s = s * ((i & 1) ? (1000000008 - p[2]) : (1000000008 - p[1])) % qy;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

7.10

今天不打模拟赛,自己做题
做了几道期望dp,几道水水的数学题
中途去听了%%% H z y o i Hzyoi Hzyoi讲课
因为是线段树基础,也没怎么讲树套树、主席树之类的,没什么意思
随便进来码几句话,走了

7.11

今天有点傻。。
显然的50分暴力被我硬生生弄成了20分。。。
10+10+0=20
%%%Captain ZJ z y y zyy zyy又ak了

T1

显然的10分暴力,随手一个dfs+floyd扔上去,本来想找找规律打打表,发现没法弄
Code:

#include <bits/stdc++.h>
#define maxn 110
#define inf 1e9
using namespace std;
int dis[maxn][maxn], n, K, ans, T, qy, cnt, dist[maxn][maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

int ksm(int n, int k){
    if (!k) return 1;
    int sum = ksm(n, k >> 1);
    sum = 1LL * sum * sum % qy;
    if (k & 1) sum = 1LL * sum * n % qy;
    return sum;
}

void Do(){
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j) dist[i][j] = dis[i][j];
    for (int k = 1; k <= n; ++k)
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                if (i != k && j != k && i != j && dist[i][k] < inf && dist[k][j] < inf)
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
    for (int i = 1; i <= n; ++i)
        for (int j = i + 1; j <= n; ++j)
            if (dist[i][j] < inf) (ans += ksm(dist[i][j], K)) %= qy;
}

void dfs(int i, int j){
    if (i >= n) Do(); else
    if (j < n){
        dis[i][j] = dis[j][i] = inf;
        dfs(i, j + 1);
        dis[i][j] = dis[j][i] = 1;
        dfs(i, j + 1);
        dis[i][j] = dis[j][i] = 2;
        dfs(i, j + 1);
    } else{
        dis[i][j] = dis[j][i] = inf;
        dfs(i + 1, i + 2);
        dis[i][j] = dis[j][i] = 1;
        dfs(i + 1, i + 2);
        dis[i][j] = dis[j][i] = 2;
        dfs(i + 1, i + 2);
    }
}

void work(){
    ans = 0;
    dfs(1, 2);
    printf("%d\n", ans);
}

int main(){
    T = read(), qy = read();
    while (T--){
        n = read(), K = read();
        work();
    }
    return 0;
}
T2

又是显然的10分暴力,floyd即可
这时主要复杂度是 O ( n 3 ) O(n^3) O(n3)
后来我想出了 O ( n 2 ) O(n^2) O(n2)的遍历方法,对,就是dfs一下就好惹
然而我傻傻的把异或结果用快排排序?
复杂度因为快排变成了 O ( m l o g m ) , m = n 2 O(mlogm),m=n^2 O(mlogm),m=n2
我还信誓旦旦地算了一下, n &lt; = 4000 n&lt;=4000 n<=4000的话差不多可以过,不过没想到 n &lt; = 5000 就 是 n = 5000 n&lt;=5000就是n=5000 n<=5000n=5000,真是不给面子
吐槽的同时,我突然想到,这不是桶排就可以了吗?
蠢哭了,白白丢掉了30分

中途还想过用trie做,因为我联想到了 异 或 粽 子 异或粽子 这道题,后来还是放弃了
放一下可以40分却只有10分的沙雕Code:

#include <bits/stdc++.h>
#define maxn 5010
#define maxm 25000010
using namespace std;
struct Edge{
	int to, next;
}edge[maxm];
int n, m, Q, cnt, a[maxm], num, head[maxn], vis[maxn];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void addedge(int x, int y){ edge[++num].to = y, edge[num].next = head[x], head[x] = num; }

void dfs(int u, int pre, int base){
	vis[u] = 1;
	a[++cnt] = base ^ u;
	for (int i = head[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if (v != pre && !vis[v]) dfs(v, u, base);
	}
}

int main(){
	n = read(), m = read(), Q = read();
	for (int i = 1; i <= m; ++i){
		int x = read(), y = read();
		addedge(x, y);
	}
	for (int i = 1; i <= n; ++i){
		for (int j = 1; j <= n; ++j) vis[j] = 0;
		dfs(i, 0, i);
	}
	sort(a + 1, a + 1 + cnt);
	while (Q--){
		int K = read();
		printf("%d\n", a[cnt + 1 - K]);
	}
	return 0;
}
T3

这题,我不想说什么
这真是属于那种暴力都打不出来的题目
一开始我竟然还想着打打 A ∗ A* A,后来还是放弃了
Code:

哈哈太难了我没打

在变菜的道路上一路狂奔

7.12

今天的题目看起来都很可做
我的开题顺序是321,哈哈没想到吧
10+10+30=50
还是有点遗憾,对于我自己可打的暴力分有没有打满

T1

初看是感觉不可做的
所以看后面题目去了,我是打了后面暴力回来顺手打的

顺手一个无脑暴力10分, T &lt; = 1 e 16 T&lt;=1e16 T<=1e16有点懵,发现以 2 m 2^m 2m为周期,这就没问题了

正解貌似是数位DP,什么鬼?
Code:

#include <bits/stdc++.h>
#define maxn 1010
#define LL long long
using namespace std;
LL n, m, s, t, power[maxn], a[maxn], tot[maxn];

inline LL read(){
    LL s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

int nxt(int x){ return (x + 1) % power[m]; }

int main(){
    n = read(), m = read(), s = read(), t = read();
    power[0] = 1;
    for (int i = 1; i <= m; ++i) power[i] = power[i - 1] << 1;
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = 1; i <= power[m]; ++i){
        tot[i] = tot[i - 1];
        LL sum = 0;
        for (int j = 1; j <= n; ++j) sum ^= a[j] = nxt(a[j]);
        tot[i] += (sum == s);
    }
    printf("%lld\n", t / power[m] * tot[power[m]] + tot[t % power[m]]);
    return 0;
}
T2

又是一个无脑10分暴搜的暴力
复杂度反正是 O ( 边 数 ! ) O(边数!) O(!)

但是我竟然没发现30分依然可打!
这是可二分的
直接二分答案,改变边权,令 l e n &gt; = m i d len&gt;=mid len>=mid的边 l e n = 1 len=1 len=1 l e n &lt; m i d len&lt;mid len<mid的边 l e n = 0 len=0 len=0,跑最短路check即可,这个暴力没打真是遗憾

10分Code:

#include <bits/stdc++.h>
#define maxn 100010
#define inf 1e8
using namespace std;
struct Edge{
    int to, next, len;
}edge[maxn << 1];
int num, head[maxn], a, b, n, m, Q;
int vis[maxn << 1], ans;
inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

int Num(int x){ return (x + 1) >> 1; }
void addedge(int x, int y, int z){ edge[++num] = (Edge) { y, head[x], z }; head[x] = num; }

void dfs(int u, int fi, int se){
    if (u == b) ans = min(ans, se); else{
        for (int i = head[u]; i; i = edge[i].next){
            int v = edge[i].to;
            if (!vis[Num(i)]){
                vis[Num(i)] = 1;
                if (edge[i].len > fi) dfs(v, edge[i].len, fi); else
                if (edge[i].len > se) dfs(v, fi, edge[i].len); else dfs(v, fi, se);
                vis[Num(i)] = 0;
            }
        }
    }
}

int main(){
    n = read(), m = read(), Q = read();
    for (int i = 1; i <= m; ++i){
        int x = read(), y = read(), z = read();
        addedge(x, y, z); addedge(y, x, z);
    }
    while (Q--){
        ans = inf;
        memset(vis, 0, sizeof(vis));
        a = read(), b = read();
        dfs(a, 0, 0);
        printf("%d\n", ans >= inf ? -1 : ans);
    }
    return 0;
}
T3

这个题目,一看就很良心啊,特意设置了一些子任务,这不是我最喜欢的吗
10分暴力显然,跑两遍bfs处理出时间即可
10分 m = n − 1 m=n-1 m=n1,这是一棵树,这我知道,以1为根建树,所有1的儿子看做1的分支,我只能干掉其中一个分支,其他的都是对方的,所以建树过程中记一个size就可以把答案求出
10分 m = n m=n m=n,这是一颗基环树,因为我只打过一道基环树的题(而且还是对着代码抄的),感觉打不出,但还是打了一打,打了个我自己理解中的基环树:第一步肯定是要把环给抠出来没问题吧,我就跑了个dfs,然后环外的一堆小树我再用dfs跑了一下处理出size
我还知道点1可能在树上或者环上,所以我进行分类讨论

  • 1在树上:只给他他的子树,其他我拿走;或者干掉他其中一个分支,剩下的给他
    a n s = m i n ( s i z e [ 1 ] , n − m a x ( s i z e [ 点 1 的 儿 子 ] ) ) ans=min(size[1],n-max(size[点1的儿子])) ans=min(size[1],nmax(size[1]))
  • 1在环上:有点麻烦,一种方法就是干掉一个分支,还有一种方法我也选择环上的一个点作为我的基地,然后和他对抗,若我的基地在环上,则令 l e n 表 示 我 的 基 地 顺 时 针 方 向 ( 或 者 逆 时 针 ) 与 1 相 距 l e n len表示我的基地顺时针方向(或者逆时针)与1相距len len1len
    a n s = m i n ( n − m a x ( s i z e [ 点 1 的 儿 子 ] ) , n − s u m [ 环 长 − l e n + 1 2 ] − s u m [ l e n − 1 2 ] ) ans=min(n-max(size[点1的儿子]),n-sum[\frac{环长-len+1}{2}]-sum[\frac{len-1}{2}]) ans=min(nmax(size[1]),nsum[2len+1]sum[2len1])

30分超长Code:

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
struct Edge{
    int to, next;
}edge[maxn << 1];
int num, head[maxn], dep[maxn], d[maxn], maxnum, size[maxn], n, m;
queue<int> q;
int vis[maxn], sum[maxn], cnt1, rt[maxn], complete, cnt, pre[maxn], fa[maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y){ edge[++num] = (Edge) { y, head[x] }; head[x] = num; }

void do1_bfs(){
    q.push(1);
    while (!q.empty()){
        int u = q.front(); q.pop();
        for (int i = head[u]; i; i = edge[i].next){
            int v = edge[i].to;
            if (!d[v] && v != 1){
                d[v] = d[u] + 1; q.push(v);
            }
        }
    }
}

void do1_Bfs(int u){
    q.push(u);
    while (!q.empty()){
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = edge[i].next){
            int v = edge[i].to;
            if (!dep[v] && v != u){
                dep[v] = dep[x] + 1; q.push(v);
            }
        }
    }
}

void do1(){
    int ans = n;
    do1_bfs();
    for (int i = 2; i <= n; ++i){
        for (int j = 1; j <= n; ++j) dep[j] = 0;
        do1_Bfs(i);
        int sum = 0;
        for (int j = 1; j <= n; ++j) sum += d[j] < dep[j];
        ans = min(ans, sum);
    }
    printf("%d\n", ans);
}

void do2_dfs(int u, int pre){
    size[u] = 1;
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (v != pre){
            do2_dfs(v, u); size[u] += size[v];
        }
    }
    if (pre == 1) maxnum = max(maxnum, size[u]);
}

void do2(){
    do2_dfs(1, 0);
    printf("%d\n", n - maxnum);
}

void do3_dfs(int u){
    if (complete) return;
    if (vis[u]){
        rt[++cnt] = u;
        for (int v = pre[u]; v != u; v = pre[v]) rt[++cnt] = v;
        complete = 1; return;
    }
    vis[u] = 1;
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (v != pre[u]){
            pre[v] = u; do3_dfs(v);
        }
    }
}

void do3_Dfs(int u){
    size[u] = 1;
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (!vis[v] && v != fa[u]){
            fa[v] = u;
            do3_Dfs(v);
            size[u] += size[v];
        }
    }
}

int Nxt(int x){ return x % cnt + 1; }

void do3(){
    do3_dfs(1);
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= cnt; ++i) vis[rt[i]] = 1;
    for (int i = 1; i <= cnt; ++i) do3_Dfs(rt[i]);
    if (!vis[1]){
        int ans = size[1];
        for (int i = head[1]; i; i = edge[i].next){
            int v = edge[i].to;
            if (v != fa[1]) ans = min(ans, n - size[v]);
        }
        printf("%d\n", ans);
    } else{
        int ans = n;
        for (int i = head[1]; i; i = edge[i].next){
            int v = edge[i].to;
            if (!vis[v]) ans = min(ans, n - size[v]);
        }
        int k = 1;
        while (rt[k] != 1) ++k;
        k = Nxt(k);
        while (rt[k] != 1){
            ++cnt1;
            sum[cnt1] = sum[cnt1 - 1] + size[rt[k]];
            k = Nxt(k);
        }
        for (int len = 1; len <= cnt1; ++len) ans = min(ans, n - sum[(cnt1 - len + 1) / 2 + len] + sum[(len - 1) / 2]);
        printf("%d\n", ans);
    }
}

int main(){
    n = read(), m = read();
    for (int i = 1; i <= m; ++i){
        int x = read(), y = read();
        addedge(x, y); addedge(y, x);
    }
    if (n <= 5000) do1(); else 
    if (n == m) do3(); else do2();
    return 0;
}

7.13

由于神仙去 N O I NOI NOI了所以我们开始训练联赛内容
今天终于上100了(滑稽): 100 + 10 + 40 = 150 100+10+40=150 100+10+40=150
暴力可能打满了

T1

这道题挺简单,初看感觉有点难,后来发现我可以藐视部分分,因为我直接想出了满分做法。。
使用种族并查集就行了
把图复制一份变成两层
对于一条边 ( x , y ) (x,y) (x,y),长度是奇数就把 ( x , y + n ) , ( x + n , y ) 合 并 起 来 (x,y+n),(x+n,y)合并起来 (x,y+n),(x+n,y)
如果是偶数就把 ( x , y ) , ( x + n , y + n ) (x,y),(x+n,y+n) (x,y),(x+n,y+n)合并起来
询问只要 g e t ( x ) = = g e t ( y ) 或 g e t ( x + n ) = = g e t ( y + n ) get(x)==get(y)或get(x+n)==get(y+n) get(x)==get(y)get(x+n)==get(y+n)就行了

Code:

#include <bits/stdc++.h>
#define maxn 1000010
using namespace std;
int n, q, f[maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

int get(int k){ return k == f[k] ? k : f[k] = get(f[k]); }

void merge(int x, int y){
    int s1 = get(x), s2 = get(y);
    if (s1 != s2) f[s1] = s2;
}

int main(){
    n = read(), q = read();
    for (int i = 1; i <= (n << 1); ++i) f[i] = i;
    while (q--){
        int opt = read(), x = read(), y = read(), z = read();
        if (opt == 1){
            if (z & 1) merge(x, y + n), merge(x + n, y); else
            merge(x, y), merge(x + n, y + n);
        } else
        if (get(x) == get(y) || get(x + n) == get(y + n)) puts("YES"); else puts("NO");
    }
    return 0;
}
T2

一开始感觉T2是最可做的,就一直在想T2,没想出来
最后只能一个暴力莽上去
毫无悬念的10分
Code:

#include <bits/stdc++.h>
#define maxn 1010
#define LL long long
#define qy 1000000007
using namespace std;
priority_queue < int, vector<int>, greater<int> > q;
int n, k, a[maxn], id[maxn], vis[maxn], ans;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void Do(){
    for (int i = 1; i <= k; ++i) q.push(a[id[i]]);
    int time = 0;
    for (int i = k + 1; i <= n; ++i){
        (ans += (time += q.top()) %= qy) %= qy; q.pop();
        q.push(a[id[i]]);
    }
    while (!q.empty()){
        (ans += (time += q.top()) %= qy) %= qy; q.pop();
    }
}

void dfs(int k){
    if (k > n) Do(); else{
        for (int i = 1; i <= n; ++i)
            if (!vis[i]){
                vis[i] = 1, id[k] = i;
                dfs(k + 1);
                vis[i] = 0;
            }
    }
}

void did(){
    memset(vis, 0, sizeof(vis));
    ans = 0;
    n = read(), k = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    dfs(1);
    printf("%d\n", ans);
}

int main(){
    did();
    return 0;
}

然后我订正好了这道题
题解传送门

T3

初看感觉不可做
发现20分暴力随手来
又发现一个小规律
如果把一个 P 1 P_1 P1确定, P i ( i &gt; 1 ) P_i(i&gt;1) Pi(i>1)的取值只能是 P i − l o w b i t ( i − 1 ) P_{i-lowbit(i-1)} Pilowbit(i1) x o r xor xor x ( l o w b i t ( i − 1 ) &lt; = x &lt; 2 ∗ l o w b i t ( i − 1 ) ) x(lowbit(i-1)&lt;=x&lt;2*lowbit(i-1)) x(lowbit(i1)<=x<2lowbit(i1))
别问我是怎么发现规律的。。

用这个玩意儿去优化一下暴力,发现可以过 k = 4 k=4 k=4情况,复杂度的话,应该是 O ( 能 过 40 分 ) O(能过40分) O(40)
嘿嘿
开心的40分Code:

#include <bits/stdc++.h>
#define maxn 1010
#define inf 2147483647
using namespace std;
int a[maxn], n, k, ans, w[maxn][maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

int lowbit(int x){ return x & -x; }

void Do(){
    int sum = 0;
    for (int i = 1; i < n; ++i) sum += w[a[i]][a[i + 1]];
    ans = min(ans, sum);
}

void dfs(int k){
    if (k > n) Do(); else{
        int x = lowbit(k - 1);
        for (int i = x; i < (x << 1); ++i){
            a[k] = a[k - x] ^ i;
            dfs(k + 1);
        }
    }
}

int main(){
    k = read();
    n = 1;
    for (int i = 1; i <= k; ++i) n <<= 1;
    for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) w[i][j] = read();
    ans = inf;
    for (int i = 0; i < n; ++i){
        a[1] = i; dfs(2);
    }
    printf("%d\n", ans);
    return 0;
}

Update:然后,我就订正好了,什么情况呢,我本来可以A掉的
这个规律应该是个bug,能秒std你信不信?
题解传送门

7.14

今天打了luogu月赛
传送门
其他倒是真没干什么事情

7.15

今天学图论,但是差分约束系统还是有些没搞懂,
tarjan还没学
做了五道题目,都写了题解,所以这边也不阐述了

明天参加NOI同步赛了

7.16

今天打了同步赛
传送门

7.17

今天讲的是数学相关,会的还是会的,不会的还是不会
Hzyoi讲的有点快

下午衢阿中的acm模拟赛,我跑过去抢了道一血又跑掉了,最后成为全场罚时最短选手
做了一些数学相关的题吧

7.18

今天有点困,状态一般~~
打了同步赛
传送门
这几天都没什么好写的,因为都是自己写题
明天打模拟赛了,又可以写游记了

7.19

今天没考好,感觉题目有点难
这是我不擅长的题型=-=
得分25+20+30=75,都是最低级的暴力

T1

这是一道构造题?
想了好久想不出怎么打,只能搞搞暴力
弄了个复杂度爆炸的近似背包的暴力,大样例T掉
运气好最终拿了25分

Code:

#include <bits/stdc++.h>
#define maxn 2000010
#define maxm 110
#define int long long
using namespace std;
int S, n, vis[maxn], sum, m, a[maxn], s, dp[maxn], print[maxn], pre[maxn];

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

signed main(){
	freopen("sum.in", "r", stdin);
	freopen("sum.out", "w", stdout);
	S = read();
	n = read();
	for (int i = 1; i <= n; ++i){
		int x = read();
		vis[x] = 1, sum += x;
	}
	for (int i = 1; i < S; ++i)
		if (!vis[i]) a[++m] = S - i;
	s = sum - n;
	dp[0] = 1;
	for (int i = 1; i <= m; ++i)	
		for (int j = s; j >= a[i]; --j)
			if (dp[j - a[i]] && !dp[j]) dp[j] = 1, pre[j] = j - a[i];
	int cnt = 0;
	while (pre[s]) print[++cnt] = S - (s - pre[s]), s = pre[s];
	print[++cnt] = S - (s - pre[s]);
	printf("%d\n", cnt);
	for (int i = 1; i <= cnt; ++i) printf("%d ", print[i]);
	return 0;
}

这其实是道水题,我醉了
题解传送门

T2

也是想了好久,又没想出来,没办法,只能打暴力
无脑dfs暴力拿20分

Code:

#include <bits/stdc++.h>
#define maxn 110
using namespace std;
struct Edge{
	int to, next;
}edge[maxn << 1];
struct Line{
	int x, y;
}line[maxn];
int num, head[maxn], Max, n, n2, rt;
int w[maxn][maxn];
double ans;

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

void addedge(int x, int y){ edge[++num] = (Edge){ y, head[x] }; head[x] = num; }

void dfs(int u, int pre, int sum){
	if (sum > Max) Max = sum, rt = u;
	for (int i = head[u]; i; i = edge[i].next){
		int v = edge[i].to;
		if (v != pre) dfs(v, u, sum + w[u][v]);
	}
}

void Do(){
	Max = 0;
	dfs(1, 0, 0);
	Max = 0;
	dfs(rt, 0, 0);
	ans += 1.0 * Max / n2;
}

void dfs(int k){
	if (k >= n) Do(); else{
		w[line[k].x][line[k].y] = w[line[k].y][line[k].x] = 2;
		dfs(k + 1);
		w[line[k].x][line[k].y] = w[line[k].y][line[k].x] = 1;
		dfs(k + 1);
	}
}

int main(){
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	n = read(); n2 = 1;
	for (int i = 1; i < n; ++i){
		line[i].x = read(), line[i].y = read();
		w[line[i].x][line[i].y] = w[line[i].y][line[i].x] = 1;
		addedge(line[i].x, line[i].y); addedge(line[i].y, line[i].x);
		n2 <<= 1;
	}
	dfs(1);
	printf("%.7f\n", ans);
	return 0;
}
T3

依然是想了好久,却仍然没想出来
无奈只能打了个30分暴力
中途样例出锅不过没关系

Code:

#include <bits/stdc++.h>
#define maxn 3010
using namespace std;
int f[maxn][maxn], n, m, flag, cnt, C;

inline int read(){
	int s = 0, w = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
	for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
	return s * w;
}

int get(int x, int k){ return k == f[x][k] ? k : f[x][k] = get(x, f[x][k]); }

void merge(int opt, int x, int y){
	int s1 = get(opt, x), s2 = get(opt, y);
	if (s1 != s2) f[opt][s1] = s2;
}

bool check(int opt, int x, int y){
	int s1 = get(opt, x), s2 = get(opt, y);
	return s1 == s2;
}

int main(){
	freopen("history.in", "r", stdin);
	freopen("history.out", "w", stdout);
	n = read(), m = read();
	for (int i = 0; i < n; ++i) f[0][i] = i;
	while (m--){
		char c = getchar();
		for (; c != 'R' && c != 'T' && c != 'K'; c = getchar());
		if (c == 'R'){
			int x = read(), y = read();
			++cnt;
			for (int i = 0; i < n; ++i) f[cnt][i] = f[cnt - 1][i];
			if (flag) x = (x + C) % n, y = (y + C) % n;
			merge(cnt, x, y);
		} else
		if (c == 'K') C = read(); else{
			int x = read(), y = read(), t = read();
			if (check(cnt, x, y) && !check(max(0, cnt - t), x, y)){
				puts("Y"); flag = 0;
			} else{
				puts("N"); flag = 1;
			}
		}
	}
	return 0;
}

7.20

今天考的不错,本来以为自己切了两题,结果一题爆零了,后来发现自己是真的打错了
稍微的小遗憾
期望得分:100+100+70=270
实际得分:100+0+70=170

T1

开始很懵逼,因为样例解释写错了,所以我就反反复复读了不知道多少遍题目,然后花了好久去揣摩出题人的心理,做了好久的语文阅读题,分析了各个地方最终得到一个结论:出题人只是手误把样例解释写错了。。。
好吧浪费了这么久得想题了
最最最开始看一下数据范围, n n n 30 W 30W 30W a i a_i ai 1 0 9 10^9 109,所以不管干嘛先离散
没啥头绪,所以先打了个60分暴力
然后惊奇的发现了一个妙不可言的性质:假如我们拿着一个离散好的数列,先把 &lt; = 4 &lt;=4 <=4的数都拿出排序放回去,那么接下来把 &lt; = 3 &lt;=3 <=3的数拿出来做就是没意义的,所以只用记录一下之前的询问的数最大值是什么就行了
然后我就用这个性质对我的暴力进行了剪枝
后来我灵光乍现,想到了正解,把一堆数拿出来排序,那么他们的内部肯定是没有逆序对了,所以现在的逆序对肯定是这拿出来的一堆数跟那些很大的数中产生逆序对
直接预处理出某个数如果在原地不动对答案产生的贡献,可以用我的本命算法树状数组解决,然后弄个后缀和,每个询问对应的答案就都有了,然后就直接来了。。。
进行了对拍,没毛病
试后我无聊把暴力测了一下,神奇的是,过了!
天哪,我的暴力跑得跟正解一样快,这是什么一个情况
说明数据太弱了。。。

Code:

//ModestCoder
#include <bits/stdc++.h>
#define maxn 300010
#define LL long long
using namespace std;
struct node{
    int x, r, id;
}a[maxn];
LL tree[maxn], sum[maxn];
int n, m, p;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

bool cmp(node x, node y){ return x.r < y.r; }
void update(int x){ for (; x; x -= x & -x) ++tree[x]; }
int query(int x){ int sum = 0; for (; x <= p; x += x & -x) sum += tree[x]; return sum; }

int main(){
    n = read(), m = read();
    for (int i = 1; i <= n; ++i) a[i].r = read(), a[i].id = i;
    sort(a + 1, a + 1 + n, cmp);
    a[0].r = a[1].r + 1;
    for (int i = 1; i <= n; ++i) a[a[i].id].x = a[i].r == a[i - 1].r ? p : ++p;
    for (int i = n; i; --i){
        sum[a[i].x] += n - i - query(a[i].x);
        update(a[i].x);
    }
    for (int i = p - 1; i; --i) sum[i] += sum[i + 1];
    int Max = 0;
    LL ans = sum[1];
    printf("%lld\n", ans);
    while (m--){
        int k = read();
        if (a[k].x <= Max){
            printf("%lld\n", ans); continue;
        }
        Max = a[k].x;
        ans = sum[a[k].x + 1];
        printf("%lld\n", ans);
    }
    return 0;
}
T2

这题是我一开始以为最可做的
结果我拿了零分的好成绩

非常明显的两个部分分,也不说了
我发现了一个奇妙的性质:
最优解任意一条从根到叶子的链上的棒子高度不上升

然后我突然想到一个贪心方法,先把棒子高度排序,再用优先队列把候选存下来,然后把棒子插到候选里面去
这样子复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)
我非常自信的直接交了这个,本来打的部分分都没交,结果全崩,因为我的方法是错的,其实贪心本来可以骗个54分,结果因为数据打包,爆了零。。。

Code:

//ModestCoder
#include <bits/stdc++.h>
#define maxn 200010
using namespace std;
struct Edge{
    int to, next;
}edge[maxn << 1];
struct node{
    int val, h;
    bool operator < (const node &x) const{ return x.h > h; }
};
int n, m, ans, num, head[maxn], h[maxn], w[maxn], vis[maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y){ edge[++num] = (Edge){ y, head[x] }; head[x] = num; }
bool cmp(int x, int y){ return x > y; }

void do1(){
    if (h[1] < w[1]){
        ans = w[1] - h[1];
        sort(h + 2, h + 1 + n, cmp);
        for (int i = 2; i <= m; ++i)
            if (h[i] < w[i]){ ans = -1; break; }
        printf("%d\n", ans);
    } else{
        sort(h + 2, h + 1 + n, cmp);
        for (int i = 2; i <= m; ++i)	
            if (h[i] < w[i]) 
                if (!ans) ans = w[i] - h[i]; else{
                    ans = -1; break;
                }
        printf("%d\n", ans);
    }
}

void do2(){
    for (int i = 1; i <= m; ++i)
        if (h[i] < w[i])
            if (!ans) ans = w[i] - h[i]; else{
                ans = -1; break;
            }
    printf("%d\n", ans);
}

void do3(){
    priority_queue <node> q;
    q.push((node) { 1, h[1] });
    int cnt = 0;
    while (!q.empty()){
        node tmp = q.top(); q.pop();
        int u = tmp.val;
        vis[u] = 1;
        if (++cnt > m) break;
        if (tmp.h < w[cnt])
            if (!ans) ans = w[cnt] - tmp.h; else{
                ans = -1; break;
            }
        for (int i = head[u]; i; i = edge[i].next){
            int v = edge[i].to;
            if (!vis[v]) q.push((node) { v, h[v] });
        }
    }
    printf("%d\n", ans);
}

int main(){
    n = read();
    for (int i = 1; i <= n; ++i) h[i] = read();
    int juhua = 1, lian = 1;
    for (int i = 1; i < n; ++i){
        int x = read(), y = read();
        if (y != x + 1) lian = 0;
        if (x != 1 && y != 1) juhua = 0;
        addedge(x, y); addedge(y, x);
    }
    m = read();
    if (m > n){
        puts("-1"); return 0;
    }
    for (int i = 1; i <= m; ++i) w[i] = read();
    sort(w + 1, w + 1 + m, cmp); 
    if (juhua) do1(); else
    if (lian) do2(); else do3();
    return 0;
}
T3

这道题本来没怎么想,看到70分可以用 O ( n 2 l o g n ) O(n^2logn) O(n2logn)水过就没理想的打了70分
正解目前还没搞懂,不过可以搞
70分的话就是模仿 O ( n l o g n ) O(nlogn) O(nlogn)的最长不下降子序列的求法,只不过这道题改成了导弹拦截

Code:

//ModestCoder
#include <bits/stdc++.h>
#define maxn 3010
using namespace std;
int ans, d[maxn], r[maxn], h[maxn], n, Q, cnt;
struct node{
    int r, h;
}a[maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

bool cmp(node x, node y){ return x.r == y.r ? x.h > y.h : x.r > y.r; }

int find(int x){
    int l = 1, r = ans, sum = 0;
    while (l <= r){
        int mid = (l + r) >> 1;
        if (d[mid] >= x) sum = mid, r = mid - 1; else l = mid + 1;
    }
    return sum;
}

int main(){
    n = read(), Q = read();
    for (int i = 1; i <= n; ++i) r[i] = read(), h[i] = read();
    while (Q--){
        int A = read(), B = read();
        cnt = 0;
        for (int i = 1; i <= n; ++i)	
            if (r[i] >= A && h[i] <= B) a[++cnt] = (node){ r[i], h[i] };
        sort(a + 1, a + 1 + cnt, cmp);
        for (int i = 1; i <= ans; ++i) d[i] = 0;
        ans = 0;
        for (int i = 1; i <= cnt; ++i){
            int tmp = find(a[i].h);
            if (!tmp) d[++ans] = a[i].h; else d[tmp] = a[i].h;
        }
        printf("%d\n", ans);
    }
    return 0;
}

把T3订正好了,可以移步题解

7.21

在宾馆待了一天,做了几道题目就完了

7.22

今天讲树相关,讲课人是一个文化课神仙zzh
听的有一点点懵
然后我为了强制自己打起精神,中途上去秒了一道树上差分的水题qwq
是不是各位神仙觉得太水了打算让菜鸡上去讲?
下午自己做题

7.23

今天自己练习
打了一堆模板,以及几道树相关的题目
找回了些许数据结构的手感

7.24

今天讲了DP
感觉挺有收获的
好几道题目我自己秒掉了,但是不想上去讲
在场的还是有很多神仙的,况且我自己其实也不是特别确定我的做法正确与否
有一个人跟我同一个宾馆的,秒了好多题,好像挺厉害,但是他在机房真的好烦啊,还好我能做到自动屏蔽他的声音
还是做了几道题目

7.25

早上去vj的模拟赛凑了个热闹,题目有点难,所以后面两题我看了题解,然后我就把比赛ak掉了
看来他们上午都在上培训班或者刷暑假作业
下午做了几道题
然后就没了

后文转移至集训总结

两个原因:

  • 3.5W字了,太长了,得换篇博写写
  • 最后的总结,比较重要吧
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值