[比赛|考试] 9月第一周的考试

Sept 8th Sat

牛客网普及组赛(360pts/400pts)(100/100/100/60)

T1水题不放代码,来个链接
T2乱膜膜就行了,注意膜出来是0要变成N,还有开longlong比较保险。代码不放了,来个链接
T3给定一个括号序列,求删除若干括号使得序列合法的删除情况(全删不算)。递推/dp,令f[i][j]代表前i个括号,匹配深度为j的方案数,(一种f[i][j]=f[i-1][j]+f[i-1][j-1])一种f[i][j]=f[i-1][j]+f[i-1][j+1],注意'('的j=0不从-1转移,然后写个滚动数组,代码
T4莫名其妙搞了60分,坐等题解

Sept 7th Fri

Contest 1

下午考试

100pts(10/30/60)

炸题大作战(这次不想鞭代码了)

T1:给定一数列:\(F[1]=F[2]=1,F[n]=\left\{\begin{aligned}F[n-1]+F[n-2](2\not | n)\\F[n-1]-F[n-2](2|n)\end{aligned}\right.\)
求数列的第N项,膜\(\color{red}100000007\),N对于60%是10的6,,90%是10的16,100%是10的200。。。
然后呢我就先敲了个很水的矩阵快速幂,然后开始为那一个点钻研
然后那个点其实很水的,用字符串读入,二进制随便搞搞就行了
然后我10pts(不敢相信),题里是1e7+7,我顺手打了个1e9+7(zz的我还jb数着,1,2,3,4,5,6,7,8,9,嗯9个0,按下退格,敲个7),和我一样被卡的还有炸同学大佬(把下面代码的1000000007改为10000007就对了)

T2:给定一无向图求满足第1个点最多连出k条边的MST,这题我先写了个先不考虑1号点和它联的边,然后在其他点上跑出k个连通块,然后把这些与1号点连接的边加入再跑MST,然鹅30分
能AC的解法是直接MST,如果与第1个点连边达到k条,不考虑这个。这么写能AC但是有反例:

4 5 2
1 2 1
1 3 1
2 3 1
1 4 100
3 4 10000

然后呢这个如果按照"AC解法"就会被卡,因为您第一次会选1-2和1-3边,然后k用完了,您就会选3-4这条10000的边。花费达到了10002,就炸了,如果按照我写的解法,会先把3-4连上(很吼啊),然后连接1-2,1-4,这样花费只有102。
自我感觉我的写法挺对的啊,(反正那个能AC的贪心会被卡)

T3:国王游戏。高精。我他妈写了进位函数忘了调用了。白炸。

T4:输出1。莫名其妙的题(什么鬼)

没了

Contest 2

牛客练习赛36

AC4题(ACM赛制)
第一次体验ACM赛制,了解一下。。。

膜拜大佬ztb首切T6!膜拜sjzez三大佬切T5!

下面题解

T1:给定2n个相交直线,求他们把平面分成多少份
详见《信息学奥赛一本通》--五种常见的递推关系、《不开longlong见祖宗》

#include <bits/stdc++.h>
using namespace std;

int main()
{
	long long n;
	cin >> n;
	cout << (2 * n + 1) * n + 1;
	return 0;
}

T2:给定N个烟花,每个烟花有不同颜色,每个烟花有一个燃放成功的概率\(P_i\),求最后燃放成功k个烟花的概率和得到颜色数目的期望
燃放k个烟花的概率是个简单的递推
得到颜色数目嘛。。。。直接p相加就行

#include <bits/stdc++.h>
using namespace std;
#define double long double
double f[11000];
double cnt = 0;
int main()
{
	int n, k;
	scanf("%d%d", &n, &k);
	memset(f, 0, sizeof(f));
	f[0] = 1;
	for (int i = 1; i <= n; i++)
	{
		double p;
		scanf("%Lf", &p);
		cnt += p;
		for (int j = k; j >= 1; j--)
			f[j] = f[j] * (1 - p) + f[j - 1] * p;
		f[0] *= (1 - p);
	}
	printf("%.4Lf\n", cnt);
	printf("%.4Lf\n", f[k]);
	return 0;
}

T3:给一个有N个点的链,给M个有序数对\((x_i,y_i)\),你需要切掉链上的X条边使得任意的\(i\in [1,M]\)使得\(x_i,y_i\)不连通,求X最小值
M是1千万, N是100万,出题人提供快读
对于一个点x,假设他向右要与很多点切断连接,那么只要他和最近点切断连接,他就和所有点切短连接了
维护一个far表示当前一组点需要切断到最右端点的最左的一页,如果\(far\ge i\)则更新一次答案,重做一组点,让far向左更新,因为我语文是swh教的所以描述不清,具体看代码吧

#include <bits/stdc++.h>
using namespace std;

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 22)], *p1 = buf, *p2 = buf;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}

int n, m, to[1000010], ans;

int main()
{
	n = read();
	m = read();
	for (int i = 1; i <= n; i++)
		to[i] = 0x3f3f3f3f;
	for (int x, y, i = 1; i <= m; i++)
	{
		x = read();
		y = read();
		to[x] = min(to[x], y);
	}
	int far = 0;
	for (int i = 1; i <= n; i++)
	{
		if(to[i] != 0x3f3f3f3f)
		{
			if (i >= far)
				ans++, far = to[i];
			if (to[i] < far)
				far = to[i];
			
		}
	}
	printf("%d\n", ans);
	return 0;
}

T4:给定一组数\(a_i\),和一些询问\(x,y\)求是否能通过异或这些数中任意的数使得x变成y
woc线性基裸题赶紧切了

#include <bits/stdc++.h>
using namespace std;
 
int a[32];
 
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 22)], *p1 = buf, *p2 = buf;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
 
void add(int x)
{
    for (int i = 31; i >= 0; i--)
    {
        if (x & (1 << i))
        {
            if (a[i] == 0)
            {
                a[i] = x;
                break;
            }
            x ^= a[i];
        }
    }
}
 
bool query(int x)
{
    for (int i = 31; i >= 0; i--)
    {
        if (x & (1 << i))
        {
            if (a[i] == 0)
            {
                return false;
            }
            x ^= a[i];
        }
    }
    return true;
}
 
int main()
{
    int n;
    n = read();
    for (int i = 1; i <= n; i++)
    {
        add(read());
    }
    int m;
    m = read();
    for (int i = 1; i <= m; i++)
        printf("%s\n", query(read() ^ read()) ? "YES" : "NO");
    return 0;
}

T5:特别恶心的树剖,具体艾特雷哥,我雷哥社会

T6:没推出来

感想:第一次接触ACM赛制,比赛是挺紧张的额,然后呢sjzez的大佬们tql(巨大压力),(也更不知道hz大佬强到哪里呢)

Sept 6th Thu

牛客网OI赛制测试赛2

题目真心水(我的实力更tm水)

390pts(0/10/100/100/100/80)

T1:给定2个数A,B,求无序二元组\((a,b)\)满足\(a|A\)\(b|B\)的个数

交换顺序算一组,比如(1,2)和(2,1)

然后呢这题思路其实很简单

根据整数惟一分解定理,将A和B分解质因数,根据约数个数公式求出A和B的约数个数
对于重复的,就是找A和B重复的约数,就是\(\gcd(A, B)\)的约数,\(\gcd(A, B)\)的惟一分解就是A的惟一分解和B的惟一分解的相同底数的指数取min
然后呢最后\(Ans=d_A+d_B-C_{d_g}^2\)//C表示组合数,\(d_x\)表示x的约数个数

然后呢为什么我爆零了,因为这题要分解质因数嘛,\(A,B\le 10000\),于是我打了个到100多的长度为30的质数表,因为最后\(A\)\(B\)可能有一个很大的质因子,但是每个数最多有1个,于是我让第31位存A的大质因子的幂,32是B的。但是我没有考虑到这两个大质因子相等的情况,应该让他们两个在一起的,燃后就gg了,g的口脚。。。。然后呢出题人故意卡数据(其实每个点1W个数据除了买彩票都会被卡)就零分了

(clang挺好,支持中文标识符)

#include <bits/stdc++.h>
#define int long long
using namespace std;
 
int p[35] = {2333, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113};
int a[35], b[35], m[35];
 
void 崔家贺()
{
    memset(a, 0, sizeof(a));
    memset(b, 0, sizeof(b));
    memset(m, 0, sizeof(m));
    int A, B;
    scanf("%lld%lld", &A, &B);
     
    for (int i = 1; A > 1 && i <= 30; i++)
    {
        while (A % p[i] == 0)
        {
            A /= p[i];
            a[i]++;
        }
    }
    for (int i = 1; B > 1 && i <= 30; i++)
        while (B % p[i] == 0)
        {
            B /= p[i];
            b[i]++;
        }
    if (A > 1)
        a[31] = 1;
    if (B > 1)
        b[32] = 1;
    if (A == B && A > 1)
    {
        b[32] = 0;
        b[31] = 1;
    }
    for (int i = 1; i <= 32; i++)
        m[i] = min(a[i], b[i]);
    int ans1 = 1, ans2 = 1, ans3 = 1;
    for (int i = 1; i <= 32; i++)
    {
        ans1 *= a[i] + 1;
        ans2 *= b[i] + 1;
        ans3 *= m[i] + 1;
    }
    printf("%lld\n", ans1 * ans2 - ans3 * (ans3 - 1) / 2);
}
 
signed main()
{
    int t;
    scanf("%lld", &t);
    for (int i = 1; i <= t; i++)
        崔家贺();
    return 0;
}

T2:给一个邻接矩阵,求从1到N路径长度为k的路径数

为啥叫邻接矩阵,因为他有矩阵的性质,比如说矩阵的实质是线性变换的路径,矩阵乘法是路径的连接?!!!?!?WTF路径的连接,一看这题赶紧打个矩阵乘法
然后因为k比较小,就没打快速幂

然后呢交上去就10pts,因为没开memset
好吧开memset,交上去90pts,因为没开浪浪(thanks GMPotLc)

#include <bits/stdc++.h>
#define int long long
using namespace std;
  
int n;
  
struct matrix
{
    int a[500][500];
    matrix(){memset(a, 0, sizeof(a));}
};
  
matrix operator*(const matrix &x, const matrix &y)
{
    matrix ans;
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                ans.a[i][j] += x.a[i][k] * y.a[k][j];
    return ans;
}
  
signed main()
{
    matrix a;
    int k;
    scanf("%lld%lld", &n, &k);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            scanf("%lld", &a.a[i][j]);
    matrix ans;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            ans.a[i][j] = (i == j);
    for (int i = 1; i <= k; i++)
        ans = ans * a;
    printf("%lld\n", ans.a[1][n]);
    return 0;
}

十年OI一场空 不开long long见祖宗

T3:给定一数列,找到每一个数最右边第一个比这个数大的数的位置,如果没有输出0
昨天上午刚做的题,单调栈水过了

#include <bits/stdc++.h>
using namespace std;
 
int n, a[10010], ans[10010];
int s[10010], top;
 
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
    {
        while (top > 0 && a[s[top]] < a[i])
            ans[s[top--]] = i;
        s[++top] = i;
    }
    for (int i = 1; i <= n; i++)
        printf("%d%c", ans[i], i == n ? '\n' : ' ');
    return 0;
}

T4:给定一个长度为N数列一开始全是0,一共执行N次操作,第i次把编号为i的倍数的位置的数异或1,最后求数列的和
\(Ans=\lfloor\sqrt{N}\rfloor\)考虑每一个位置,他被操作的次数就是他的约数个数,当且仅当他是完全平方数的时候他有奇数个约数(考虑约数个数公式)
所以找\(1\)\(N\)内完全平方数个数为\(\lfloor\sqrt{N}\rfloor\)

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    long long n;
    scanf("%lld", &n);
    printf("%lld\n", (long long)floor(sqrt(double(n))));
    return 0;
}

T5给定一个括号序列,左右括号数相等但是不匹配,现在你可以任意次操作,每次交换两个括号,求使得括号匹配的最小交换次
(当做1,把)当做-1,求一次前缀和。每次交换一个后面的(和前面的)后会导致这一段区间前缀和+=2
所以找最小的那个前缀和,答案就是他+=2一直到>0最小时间

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
    int n;
    scanf("%d", &n);
    char ch;
    int now = 0, mins = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf(" %c", &ch);
        if (ch == ')')
            now--;
        if (ch == '(')
            now++;
        mins = min(mins, now);
    }
    printf("%d\n", (-mins + 1) / 2);
    return 0;
}

T6输入一个整数\(X\),求一个整数\(N\),使得\(N!\)恰好大于\(X^X\)。数据满足对于第i组数据输入的是连续i个7
\(N!>X^X\)两边同时取log二分打表,注意要用cpp打表,因为快,我python打了好长时间才出来8个(其实因为我对python一无所知,不知道怎么写list,tuple啥的,所以求logN!的时候直接打的暴力求加)

我打表程序

import math
tot = 77777*math.log(77777)
def fact(x):
	ans = 0
	for i in range(1, x):
		ans = ans + math.log(i + 1)
	return ans
def lis(l, r):
	if (l >= r):
		return l
	mid = (l + r) / 2
	if (fact(mid) > tot):
		return lis(l, mid)
	else:
		return lis(mid + 1, r)

用法:先给tot赋值,然后调用lis二分

教训:1.写暴力写暴力写暴力题目再水也写暴力

2.开longlong,数组初始化

3.python不透彻用cpp打表啊

Sept 3rd Mon

AK

又是NOIP原题欢乐赛。。。
这些题我都比较透彻,就全切了
T1:NOIP2015信息传递 基环树上乱搞
T2:NOIP2015子串 dp滚动数组
T3:NOIP2015斗地主 缩索+贪心

程序也不想放了

Sept 1st Sat

我忘了多少pts
NOIP原题欢乐赛。。。
T1:NOIP2014 寻找道路 切了
T2:NOIP2016 换教室 68pts???不知道哪儿炸了,把之前做的改上去了。(懒)(就某wzy大佬A了?他讲的也不透彻QAQ,然后我写一黑板的dp方程写的好tm累)
T3:NOIP2017 宝藏 85pts。。。之前看了一个题解,介绍了模拟退火算法,然后就知道这题贪心会错,就模拟退火了,然后按照自己的思路直接yy,概率函数乱写,WA了2个点,循环次数有点多电脑太慢T了1个点,然后减少了循环次数,又随便试了几个概率函数A了,然后开始瞎扯模拟退火我好像把整个机房都带歪了(其实我发现我瞎扯能力不错)

因为是原题,程序不想放了?不过模拟退火真挺有意思2333

August 31st

250pts

rank3/15

T1.传纸条

给定一个字符串,判断存在两个子串相等时候,子串的长度最长为多少,长度<=100

很简单,可以map暴力过

#include <cstdio>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>

using namespace std;

char a[1200003];
int n;
map<string, int> f;

bool pd(int x)//判断是否不存在长度为x的 
{
	f.clear();
	string str;
	for (int i = 0; i < x; i++)
		str.push_back(a[i]);
	for (int i = x; i <= n; i++)
	{
		f[str]++;
		if(f[str] >= 2)
			return false;
		if(i == n)
			return true;
		string str2 = str.substr(1);
		str2.push_back(a[i]);
		str = str2;
	}
	return true;
}

int main()
{
	freopen("message.in", "r", stdin);
	freopen("message.out", "w", stdout);
	scanf("%s", a);
	n = strlen(a);
	int l = 1, r = n;
	while(l < r)
	{
		int mid = (l + r) / 2;
		if(pd(mid))
			r = mid;
		else
			l = mid + 1;
	}
	printf("%d\n", l - 1);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2.合并序列

给定一堆序列数量k<=100,每个长度<=20万,现在让你合并,要求新序列中包含原序列的所有元素,且每个序列中元素相对位置不变,且合并后序列满足元素递减的元素位置数最小。

一开始我看错题了看TMD成逆序对了然后...各种暴力

然后我恍然大悟,但是思维改不过来了,写了一个贪心,每次在各个子序列头部找\(\ge\)当前合并后序列尾部的数且最小的数扔进来,如果没有就直接找最小的数,ans++

然后这个T了两个点,因为复杂度多了个k。正解其实只需要找每个子序列中上升序列的个数,取个max,输出减去1,因为上升的子序列可以直接合并

我的代码

#include <cstdio>
#include <iostream>
#define inf 0x3f3f3f3f3f3f3f3fLL
#define int long long
using namespace std;

struct seq
{
	int pos, n, now, x, y, p;
}a[110];

int k, len, last, ans;

void upd(int x)
{
	a[x].pos++;
	if(a[x].pos > a[x].n)
		a[x].now = inf;
	else
		a[x].now = (a[x].now * a[x].x % a[x].p + a[x].y) % a[x].p;
}

signed main()
{
	freopen("number.in", "r", stdin);
	freopen("number.out", "w", stdout); 
	scanf("%lld", &k);
	for (int i = 1; i <= k; i++)
	{
		scanf("%lld%lld%lld%lld%lld", &a[i].n, &a[i].now, &a[i].x, &a[i].y, &a[i].p);
		a[i].pos = 1;
		len += a[i].n;
	}
	last = -inf;//一开始,把last赋值极小值 
	for (int i = 1; i <= len; i++)
	{
		int m = 0;
		a[0].now = inf;
		for (int j = 1; j <= k; j++)
			if(a[j].now >= last && a[j].now < a[m].now)
				m = j;
		if(m == 0)
		{
			int mm = 0;
			for (int j = 1; j <= k; j++)
			{
				if(a[j].now < a[mm].now)
					mm = j;
			}
			last = a[mm].now;
			ans++;
			upd(mm);
			continue;
		}
		last = a[m].now;
		upd(m);
	}
	printf("%lld\n", ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

改了

#include <cstdio>
#include <iostream>
#define inf 0x3f3f3f3f3f3f3f3fLL
#define int long long
using namespace std;

struct seq
{
	int pos, n, now, x, y, p;
}a[110];

int k, len, last, ans;

void upd(int x)
{
	a[x].pos++;
	if(a[x].pos > a[x].n)
		a[x].now = inf;
	else
		a[x].now = (a[x].now * a[x].x % a[x].p + a[x].y) % a[x].p;
}

signed main()
{
	freopen("number.in", "r", stdin);
	freopen("number.out", "w", stdout); 
	scanf("%lld", &k);
	for (int i = 1; i <= k; i++)
	{
		scanf("%lld%lld%lld%lld%lld", &a[i].n, &a[i].now, &a[i].x, &a[i].y, &a[i].p);
		a[i].pos = 1;
		int last;
		int we = 0;
		while(a[i].pos != a[i].n)
		{
			last = a[i].now;
			upd(i);
			if(a[i].now < last)
				we++;
		}
		ans=max(ans,we);
	}
	printf("%lld\n", ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

cena不资磁万能头差评

T3.密码锁

70分N<=15,100分N<=1万,所以我直接状压dp了,状压dp很简单(因为昨天刚写了个愤怒的小鸟),lc写的spfa(太强啦),我的dp是\(O(2^n*n*l)\)可能慢了点??这题路径长度为1直接bfs就行了??这

#include <cstdio>
#include <iostream>
#include <cstring>
 
using namespace std;

int N, K, L, start, maxn;
int x[20], l[110], f[1234567];

int main()
{
	freopen("lock.in", "r", stdin);
	freopen("lock.out", "w", stdout);
	scanf("%d%d%d", &N, &K, &L);
	maxn = (1 << N) - 1;
	for (int i = 1; i <= K; i++)
	{
		scanf("%d", &x[i]);
		start |= (1 << (x[i] - 1));
	}
	memset(f, 0x3f, sizeof(f));
	f[start] = 0;
	for (int i = 1; i <= L; i++)
	{
		scanf("%d", &l[i]);
		for(int g = (1 << l[i]) - 1; (g | maxn) == maxn; g <<= 1)
			for (int p = 0; p <= maxn; p++)
				f[p ^ g] = min(f[p] + 1, f[p ^ g]);
	}
	if(f[0] == 0x3f3f3f3f)
		printf("-1\n");
	else
		printf("%d\n", f[0]);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

然后呢这次考试题其实不是太难,比较考察思维能力,T3实在不想改了改了就没时间tc了

然后呢这次考试是钦定四人间的???貌似能进四人间了,晚上有时间透彻了

然后呢某c和某w成绩比我高我不服(T2AC)(py交易)???

然后呢某wzy进了??

然后呢为lrt默哀3.1415936535897秒....

然后呢期待下午半天文化课把

然后呢没了(我说的有点多了)

posted @ 2018-08-31 11:22  ghj1222  阅读(241)  评论(0编辑  收藏  举报