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
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
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
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
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
#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
#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
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
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
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
#include
#include
#include
#include
T2.合并序列
给定一堆序列数量k<=100,每个长度<=20万,现在让你合并,要求新序列中包含原序列的所有元素,且每个序列中元素相对位置不变,且合并后序列满足元素递减的元素位置数最小。
一开始我看错题了看TMD成逆序对了然后...各种暴力
然后我恍然大悟,但是思维改不过来了,写了一个贪心,每次在各个子序列头部找\(\ge\)当前合并后序列尾部的数且最小的数扔进来,如果没有就直接找最小的数,ans++
然后这个T了两个点,因为复杂度多了个k。正解其实只需要找每个子序列中上升序列的个数,取个max,输出减去1,因为上升的子序列可以直接合并
我的代码
#include
#include
#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
#include
#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
#include
#include
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秒....
然后呢期待下午半天文化课把
然后呢没了(我说的有点多了)