挺好的一场比赛,结果被我考得像*一样.
第一次打这种5个小时的比赛,想着时间还比较充足,就花了比较长的时间读题,预计自己应该能得40+80+50,如果中途想到了正解分数可能会更高.初步感觉T1和T3的暴力挺好打的,只是这个T2......预感到自己将花一半的时间在这道题上.
先打T1的暴力,不到10min就打完了,测了测样例一遍就AC了,检查了一下没有发现什么问题.挺开心的,但是还是不放行,于是输出了几个中间值,发现和预期的完全不同.接着测了自己造几个小数据,都碰巧过了,但仅仅只是碰巧,中途计算的结果还是错的,如果直接这样交上去可能就没分了.于是去上了个厕所,突然想起来ST表求区间异或是错的!会把重叠部分给多统计!一开始还一直以为是ST表写错了.那么也没办法了,就打了一个线段树上去,常数大,又带一个log,当时还担心1000的数据会不会T,事实证明担心是多余的.
T2想着暴力有80分,都没去想正解(其实是不敢想,毕竟下意识里认为我做不出省选难度的第二题嘛),结果没想到正解比暴力还简单.慢慢地1h过去了,写了300+行线段树,写到身心俱疲,心力憔悴.前60%的点我造了几组小数据都过了,结果后面20%的一直写不对.改了好长时间,大概有2h吧,到最后整个思路都混乱了,但我觉得我的做法明明就是正确的,一定不会错,再给我一点时间肯定能找到错误.果然,原来是一种情况b值的变化没有考虑到,下意识地以为和前面的相同.改过来就对了,测了好几组小数据都过了.终于放心了.然而最后的结果令人大吃一惊.我认为不可能错的恰好错了,可能错的却没错.原因是:我测试的数据全部都是小数据!当我把用大数据测时,我发现我的程序输出了好多负数,绝对是爆int了.可是我的程序里基本上全都是long long,就连读入的变量都是long long,怎么会爆int呢?于是我ctrl + F查找int,发现结构体里面有几个变量定义的是int......MDZZ.可是后20%的点还是不对啊......对于答案输出和我的输出,非常奇怪,前面一些点的输出都是一样的,到后面就完全不一样了.查错多时,无果.向老师求助,老师一眼就看出了我的错误:除法取模.MDZZ.我竟然会犯这种低级错误......当时直接写的表达式,表达式一长,取模操作就容易错,真应该听老师的话:表达式拆开来写.
改完T2就没时间写T3了,其实T3的暴力分特别好拿,白丢了50分.匆忙写的dfs还WA掉了.发现我在写用dfs求最长路的时候忘了清零vis数组了......
题解:
1. 简易比特币
题目描述
相信大家都听说过比特币。它是一种虚拟货币,但与普通虚拟货币不同的是,它不由某个机构统一发行,而需要利用计算机找出具有特定性质的数据来“发现”货币,俗称“挖矿”。然而,由于具有这种特定性质的数据分布稀疏而无规律,因此挖矿的过程需要投入大量的计算资源来搜寻这些数据。
仿照比特币是设计思想,我们可以设计一种简易的比特币:给定一个由n个非负整数构成的序列{ai},和一个阈值K,如果某个非空子序列(一个连续的区间)中的所有数的异或和小于K,则这个子序列就对应了一个比特币,否则它毫无价值。
现在,给出这个序列和阈值,请你计算从中能获得多少个比特币。
严谨起见,简要解释一下什么是异或:
异或是一种位运算,Pascal中写作xor,C/C++中写作^。将两个数写成二进制形式,然后对每位作“相同得0、不同得1”的运算。例如,12 xor 6 = 10的运算方法如下:
12 = (1100)2
6 = (0110)2
ans= (1010)2 = 10
输入格式
第一行包含两个整数n和K,意义如题所述;
第二行包含n个非负整数ai,表示序列中的每一个数。
输出格式
一行包含一个整数,表示能从序列中获得的比特币数。
样例输入
3 2
1 3 2
样例输出
3
样例解释
1 = 1
1 xor 3 = 2
1 xor 3 xor 2 = 0
3 = 3
3 xor 2 = 1
2 = 2
一共3个区间的异或和小于2。
数据范围
对于20%的数据,n≤100;
对于40%的数据,n≤1000;
另有20%的数据,ai≤50;
对于100%的数据,1≤n≤105,0≤K≤109,0≤ai≤109。
分析:Trie树上一个比较神的应用.
做这道题之前需要思考另外一道题:给定n个数,再给一个数c,问有多少数小于c.这道题可以用很多方法水过,不过一种神奇的方法是利用trie+二进制.二进制数比较大小是从高位到低位比较的.将n个数都转化为二进制,插入到trie中.然后将c在trie上跑.如果c的第i位为1,则ans += 下一步走"0"边到达的点的点权(也就是这一位是0,与c不匹配了,那么通过这一位衍生出的数都小于c),c往边"1"上走,否则c直接往边"0"上走.
这道题也是一样的原理.只不过需要对问题转化一下.对于每个点,求出它的前缀异或值sum[i],那么对于一个区间[l,r]的异或值就是sum[r] ^ sum[l - 1],因为[1,l-1]这段区间被异或了2次,值为0,这也是一个非常重要的思想:区间上的值转化为两个端点的值.接着枚举每一个数,先统计答案再插入.假设当前插入的数的第i位为x,那么如果接下来走"x"边,那么下一位就为0,如果走"x^1"边,下一位就为1,依据上一题的原理,就可以统计了.
部分分解法:40%,线段树预处理出区间异或值,枚举区间,区间查询. 60%,数字很小,前缀异或值的范围是0~63,那么预处理出有多少对数的异或值小于k,然后在序列中扫,统计个数(具体看代码).
完整代码:(包含了部分分+AC代码)
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 100010; int n,k,a[maxn],tot = 1; int map[110][110],num[110]; long long sum[maxn << 2],ans,jie[40]; bool flag = true; struct node { int tr[3],sum; }e[maxn * 30]; void pushup(int o) { sum[o] = sum[o * 2] ^ sum[o * 2 + 1]; } void build(int o,int l,int r) { if (l == r) { sum[o] = a[l]; return; } int mid = (l + r) >> 1; build(o * 2,l,mid); build(o * 2 + 1,mid + 1,r); pushup(o); } long long query(int o,int l,int r,int x,int y) { if (x <= l && r <= y) return sum[o]; int mid = (l + r) >> 1; long long res = 0; if (x <= mid) res = query(o * 2,l,mid,x,y); if (y > mid) { long long temp = query(o * 2 + 1,mid + 1,r,x,y); if (res == 0) res = temp; else res ^= temp; } return res; } void solve() { sum[1] = a[1]; for (int i = 2; i <= n; i++) sum[i] = sum[i - 1] ^ a[i]; for (int i = 0; i < 64; i++) for (int j = 0; j < 64; j++) if ((i ^ j) < k) map[i][j] = 1; for (int i = 1; i <= n; i++) { for (int j = 0; j < 64; j++) { if (map[sum[i]][j]) ans += num[j]; } if (sum[i] < k) ans++; num[sum[i]]++; } cout << ans << endl; } void solve2() { build(1,1,n); for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) if (query(1,1,n,i,j) < k) ans++; for (int i = 1; i <= n; i++) if (a[i] < k) ans++; cout << ans << endl; } void insert(long long x) { int now = 1; for (int i = 30; i >= 0; i--) { bool temp = x & jie[i]; if (!e[now].tr[temp]) e[now].tr[temp] = ++tot; now = e[now].tr[temp]; e[now].sum++; } } long long query(long long x) { long long res = 0; int now = 1; for (int i = 30; i >= 0; i--) { if (now == 0) return res; bool temp = x & jie[i]; bool temp2 = temp ^ 1; if (k & jie[i]) { res += e[e[now].tr[temp]].sum; now = e[now].tr[temp2]; } else now = e[now].tr[temp]; } return res; } void solve3() { jie[0] = 1; for (int i = 1; i <= 30; i++) jie[i] = jie[i - 1] * 2; long long res = 0; insert(0); for (int i = 1; i <= n; i++) { res ^= a[i]; ans += query(res); insert(res); } cout << ans << endl; } int main() { scanf("%d%d",&n,&k); for (int i = 1; i <= n; i++) { scanf("%d",&a[i]); if (a[i] > 50) flag = false; } if (flag) solve(); //20% else if (n <= 1000) solve2(); //40% else solve3(); return 0; }
2.计算
问题描述
我曾经的竞赛教练有一句名言:“人紧张起来的时候会变得和白痴一样的。”他总爱在比赛前重复这句话。其实论算法,他并没有教给我们多少,但是回想起以前的经历发现,至少这句话他说的真是太tm对了。用现在的话讲就是:不要怂,就是干。
oi题很多时候都是这样,乍一看很难,越看越觉得不可做,于是安慰自己说,肯定又是我没学过的某算法,做不出很正常。但抱有这种心理的,出了考场往往会被身边的神犇打脸:“这题其实先oo一下再xx一下就好了,我太弱了搞了一小时才搞出来……”
现在就有一道看上去似乎很不好搞的计算题,请你不怂地算一下怎么搞。
给出一个长为N的正整数序列,有三种操作:
A l r k b:在区间[l,r]上加上一个首项为b、公差为k的等差数列。即,序列al, al+1, al+2, al+3……变成al+b, al+1+b+k, al+2+b+2k, al+3+b+3k……
B l r:求区间[l,r]内所有数的和mod 1000000007的值
C l r:求区间[l,r]内所有数的平方的和mod 1000000007的值
输入格式
第一行包含两个数n、q,表示序列长度和操作的数量;
第二行包含n个数{ai},表示原序列;
接下来q行,每行包含一个操作,格式和意义如题面所述。
输出格式
输出若干行,每个B操作和C操作输出一行,表示询问的答案。
样例输入
3 3
1 1 1
A 1 3 2 2
B 1 2
C 2 3
样例输出
8
74
数据规模
测试点1~2:n, q ≤ 1000;
测试点3~4:k=0,没有C操作;
测试点5~6:k=0;
测试点7~8:没有C操作;
对于100%的数据,n, q ≤ 100000,0 ≤ ai, k, b ≤ 109,1 ≤ l ≤ r ≤ n
分析:部分分解法:
20%:直接暴力.
40%,区间加,区间求和.
60%:区间加,区间求和,区间求平方和,把式子展开一下就好了,坑点:要先维护平方和,再维护和.
80%:等差数列区间加,区间求和. 一个标记维护两个内容:在当前区间生效的K值和B值.标记的下传:子区间的k,b直接加上父节点的k,b,不过对于右子区间的b有变化!这是需要特别注意的一点:增加b值会有偏移!
100%:通用方法:式子展开.看哪些信息已经维护了,哪些信息还需要维护.维护一下就好了.这里给出最终需要维护的标记:
完整代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll maxn = 100010,mod = 1000000007; ll n,q; bool flag1 = true,flag2 = true; ll a[maxn],sum[maxn << 2],tag[maxn << 2],L[maxn << 2],R[maxn << 2],sum2[maxn << 2],cnt[maxn << 2],K[maxn << 2],B[maxn << 2],inc[maxn << 2]; struct node { ll id,k,b,l,r; }e[maxn]; ll qumo(ll a,ll b) { if (a % 2 == 0) a /= 2; else b /= 2; a %= mod; b %= mod; return a * b % mod; } void solve1() { for (ll i = 1; i <= q; i++) { if (e[i].id == 1) { ll temp = e[i].b; for (ll j = e[i].l; j <= e[i].r; j++) { a[j] += temp; a[j] %= mod; temp += e[i].k; temp %= mod; } } if (e[i].id == 2) { ll res = 0; for (ll j = e[i].l; j <= e[i].r; j++) { res += a[j]; res %= mod; } printf("%lld\n",res); } if (e[i].id == 3) { ll res = 0; for (ll j = e[i].l; j <= e[i].r; j++) { res += a[j] * a[j] % mod; res %= mod; } printf("%lld\n",res); } } } void pushup(ll o) { sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % mod; sum2[o] = (sum2[o * 2] + sum2[o * 2 + 1]) % mod; inc[o] = inc[o * 2] + inc[o * 2 + 1] + (R[o * 2] - L[o * 2] + 1) * sum[o * 2 + 1] % mod; inc[o] %= mod; } void build(ll o,ll l,ll r) { L[o] = l; R[o] = r; if (l == r) { sum[o] = a[l]; sum[o] %= mod; sum2[o] = a[l] * a[l] % mod; return; } ll mid = (l + r) >> 1; build(o * 2,l,mid); build(o * 2 + 1,mid + 1,r); pushup(o); } void pushdown(ll o) { if (tag[o]) { tag[o * 2] += tag[o]; tag[o * 2] %= mod; tag[o * 2 + 1] += tag[o]; tag[o * 2 + 1] %= mod; ll temp1 = ((tag[o] * tag[o]) % mod) * (R[o * 2] - L[o * 2] + 1) % mod; sum2[o * 2] += temp1 + (2 * tag[o] * sum[o * 2])% mod; sum2[o * 2] %= mod; ll temp2 = ((tag[o] * tag[o]) % mod) * (R[o * 2 + 1] - L[o * 2 + 1] + 1) % mod; sum2[o * 2 + 1] += temp2 + 2 * tag[o] * sum[o * 2 + 1] % mod; sum2[o * 2 + 1] %= mod; sum[o * 2] += tag[o] * (R[o * 2] - L[o * 2] + 1) % mod; sum[o * 2] %= mod; sum[o * 2 + 1] += tag[o] * (R[o * 2 + 1] - L[o * 2 + 1] + 1) % mod; sum[o * 2 + 1] %= mod; tag[o] = 0; } } void update(ll o,ll l,ll r,ll x,ll y,ll v) { if (x <= l && r <= y) { tag[o] += v; tag[o] %= mod; ll temp = ((v * v) % mod) * (r - l + 1) % mod; sum2[o] += temp + 2 * v * sum[o] % mod; sum2[o] %= mod; sum[o] += v * (r - l + 1) % mod; sum[o] %= mod; return; } pushdown(o); ll mid = (l + r) >> 1; if (x <= mid) update(o * 2,l,mid,x,y,v); if (y > mid) update(o * 2 + 1,mid + 1,r,x,y,v); pushup(o); } ll query(ll o,ll l,ll r,ll x,ll y) { if (x <= l && r <= y) return sum[o]; pushdown(o); ll mid = (l + r) >> 1; ll res = 0; if (x <= mid) res += query(o * 2,l,mid,x,y); res %= mod; if (y > mid) res += query(o * 2 + 1,mid + 1,r,x,y); res %= mod; return res; } ll query2(ll o,ll l,ll r,ll x,ll y) { if (x <= l && r <= y) return sum2[o]; pushdown(o); ll mid = (l + r) >> 1; ll res = 0; if (x <= mid) res += query2(o * 2,l,mid,x,y); res %= mod; if (y > mid) res += query2(o * 2 + 1,mid + 1,r,x,y); res %= mod; return res; } void solve2() { build(1,1,n); for (ll i = 1; i <= q; i++) { if (e[i].id == 2) printf("%lld\n",query(1,1,n,e[i].l,e[i].r)); else if (e[i].id == 1) update(1,1,n,e[i].l,e[i].r,e[i].b); else if (e[i].id == 3) printf("%lld\n",query2(1,1,n,e[i].l,e[i].r)); } } void pushdown2(ll o) { if (cnt[o] != 0) { cnt[o * 2] = cnt[o * 2 + 1] = 1; cnt[o] = 0; K[o * 2] += K[o]; K[o * 2] %= mod; K[o * 2 + 1] += K[o]; K[o * 2 + 1] %= mod; B[o * 2] += B[o]; B[o * 2] %= mod; B[o * 2 + 1] += (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod; B[o * 2 + 1] %= mod; ll tempp = qumo(B[o] + B[o] + (R[o * 2] - L[o * 2]) * K[o],R[o * 2] - L[o * 2] + 1); sum[o * 2] += tempp; sum[o * 2] %= mod; ll temp = (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod; //右半区间的b tempp = qumo(temp + temp + (R[o * 2 + 1] - L[o * 2 + 1]) * K[o],R[o * 2 + 1] - L[o * 2 + 1] + 1); sum[o * 2 + 1] += tempp; sum[o * 2 + 1] %= mod; K[o] = 0; B[o] = 0; } } void update2(ll cur,ll o,ll l,ll r,ll x,ll y,ll k,ll b) { if (x <= l && r <= y) { cnt[o] = 1; K[o] += k; K[o] %= mod; b = b + (l - e[cur].l) * k % mod; b %= mod; B[o] += b; B[o] %= mod; ll res1 = (r - l) * k + b + b; ll res2 = r - l + 1; if (res1 % 2 == 0) res1 /= 2; else res2 /= 2; res1 %= mod; res2 %= mod; ll temp = qumo(b + b + ((r - l) * k),(r - l + 1)); temp %= mod; sum[o] += temp; sum[o] %= mod; return; } pushdown2(o); ll mid = (l + r) >> 1; if (x <= mid) update2(cur,o * 2,l,mid,x,y,k,b); if (y > mid) update2(cur,o * 2 + 1,mid + 1,r,x,y,k,b); pushup(o); } ll query3(ll o,ll l,ll r,ll x,ll y) { if (x <= l && r <= y) return sum[o]; pushdown2(o); ll res = 0; ll mid = (l + r) >> 1; if (x <= mid) res += query3(o * 2,l,mid,x,y); res %= mod; if (y > mid) res += query3(o * 2 + 1,mid + 1,r,x,y); res %= mod; return res; } void solve3() { build(1,1,n); for (ll i = 1; i <= q; i++) { if (e[i].id == 2) printf("%lld\n",query3(1,1,n,e[i].l,e[i].r)); else update2(i,1,1,n,e[i].l,e[i].r,e[i].k,e[i].b); } } void pushdown3(ll o) { if (cnt[o] != 0) { cnt[o * 2] = cnt[o * 2 + 1] = 1; cnt[o] = 0; K[o * 2] += K[o]; K[o * 2] %= mod; K[o * 2 + 1] += K[o]; K[o * 2 + 1] %= mod; B[o * 2] += B[o]; B[o * 2] %= mod; B[o * 2 + 1] += (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod; B[o * 2 + 1] %= mod; //史上最痛苦的时刻: ll len1 = (R[o * 2] - L[o * 2] + 1),len2 = (R[o * 2 + 1] - L[o * 2 + 1] + 1); ll tempp = (B[o] + ((R[o * 2] - L[o * 2] + 1) * K[o] % mod)) % mod; //右半部分的b! ll temp = 2 * B[o] * sum[o * 2] % mod; temp = (temp + 2 * K[o] * inc[o * 2] % mod) % mod; temp = (temp + B[o] * B[o] % mod * len1 % mod) % mod; temp = (temp + K[o] * B[o] % mod * (len1 - 1) % mod * len1 % mod) % mod; ll temp2 = (len1 - 1) * len1 * (2 * len1 - 1) / 6 % mod; temp2 = temp2 * K[o] % mod * K[o] % mod; temp = (temp + temp2) % mod; sum2[o * 2] = (sum2[o * 2] + temp) % mod; temp = temp2 = 0; ll k = K[o],b = tempp; temp = 2 * b * sum[o * 2 + 1] % mod; temp = (temp + 2 * k * inc[o * 2 + 1] % mod) % mod; temp = (temp + b * b % mod * len2 % mod) % mod; temp = (temp + k * b % mod * (len2 - 1) % mod * len2 % mod) % mod; temp2 = (len2 - 1) * len2 * (2 * len2 - 1) / 6 % mod; temp2 = temp2 * k % mod * k % mod; temp = (temp + temp2) % mod; sum2[o * 2 + 1] = (sum2[o * 2 + 1] + temp) % mod; temp = temp2 = 0; b = B[o],k = K[o]; temp = b * len1 % mod; temp2 = (len1 - 1) * len1 / 2 % mod; temp2 = temp2 * k % mod; temp = (temp + temp2) % mod; sum[o * 2] = (sum[o * 2] + temp) % mod; temp = temp2 = 0; b = tempp,k = K[o]; temp = b * len2 % mod; temp2 = (len2 - 1) * len2 / 2 % mod; temp2 = temp2 * k % mod; temp = (temp + temp2) % mod; sum[o * 2 + 1] = (sum[o * 2 + 1] + temp) % mod; temp = temp2 = 0; k = K[o],b = B[o]; temp = (len1 - 1) * len1 / 2 % mod; temp = temp * b % mod; temp2 = (len1 - 1) * len1 * (2 * len1 - 1) / 6 % mod; temp2 = temp2 * k % mod; temp = (temp + temp2) % mod; inc[o * 2] = (inc[o * 2] + temp) % mod; temp = temp2 = 0; k = K[o],b = tempp; temp = (len2 - 1) * len2 / 2 % mod; temp = temp * b % mod; temp2 = (len2 - 1) * len2 * (2 * len2 - 1) / 6 % mod; temp2 = temp2 * k % mod; temp = (temp + temp2) % mod; inc[o * 2 + 1] = (inc[o * 2 + 1] + temp) % mod; temp = temp2 = 0; B[o] = K[o] = 0; } } void update3(ll cur,ll o,ll l,ll r,ll x,ll y,ll k,ll b) { if (x <= l && r <= y) { b = b + (l - e[cur].l) * k % mod; b %= mod; cnt[o] = 1; K[o] += k; K[o] %= mod; B[o] += b; B[o] %= mod; ll temp = 2 * b * sum[o] % mod; temp = (temp + 2 * k * inc[o] % mod) % mod; temp = (temp + b * b % mod * (r - l + 1) % mod) % mod; temp = (temp + k * b % mod * (r - l) % mod * (r - l + 1) % mod) % mod; ll len = r - l + 1; ll temp2 = (len - 1) * len * (2 * len - 1) / 6; temp2 %= mod; ll temp3 = k * k % mod; temp = (temp + temp2 * temp3 % mod) % mod; sum2[o] += temp; sum2[o] %= mod; temp = b * len % mod; temp2 = (len - 1) * len / 2; temp2 %= mod; temp2 = temp2 * k % mod; temp = (temp + temp2) % mod; sum[o] += temp; sum[o] %= mod; temp2 = (len - 1) * len / 2; temp2 %= mod; temp2 = temp2 * b % mod; temp = temp2; temp2 = (len - 1) * len * (2 * len - 1) / 6; temp2 %= mod; temp2 = temp2 * k % mod; temp = (temp + temp2) % mod; inc[o] += temp; inc[o] %= mod; return; } pushdown3(o); ll mid = (l + r) >> 1; if (x <= mid) update3(cur,o * 2,l,mid,x,y,k,b); if (y > mid) update3(cur,o * 2 + 1,mid + 1,r,x,y,k,b); pushup(o); } ll query4(ll o,ll l,ll r,ll x,ll y) { if (x <= l && r <= y) return sum[o]; pushdown3(o); ll res = 0; ll mid = (l + r) >> 1; if (x <= mid) res += query4(o * 2,l,mid,x,y); res %= mod; if (y > mid) res += query4(o * 2 + 1,mid + 1,r,x,y); res %= mod; return res; } ll query5(ll o,ll l,ll r,ll x,ll y) { if (x <= l && r <= y) return sum2[o]; pushdown3(o); ll res = 0; ll mid = (l + r) >> 1; if (x <= mid) res += query5(o * 2,l,mid,x,y); res %= mod; if (y > mid) res += query5(o * 2 + 1,mid + 1,r,x,y); res %= mod; return res; } void solve4() { build(1,1,n); for (int i = 1; i <= q; i++) { if (e[i].id == 1) update3(i,1,1,n,e[i].l,e[i].r,e[i].k,e[i].b); else if (e[i].id == 2) printf("%lld\n",query4(1,1,n,e[i].l,e[i].r)); else printf("%lld\n",query5(1,1,n,e[i].l,e[i].r)); } } int main() { scanf("%lld%lld",&n,&q); for (ll i = 1; i <= n; i++) scanf("%lld",&a[i]); for (ll i = 1; i <= q; i++) { char ch[2]; scanf("%s",ch); if (ch[0] == 'A') { e[i].id = 1; scanf("%lld%lld%lld%lld",&e[i].l,&e[i].r,&e[i].k,&e[i].b); if (e[i].k != 0) flag1 = false; } if (ch[0] == 'B') { e[i].id = 2; scanf("%lld%lld",&e[i].l,&e[i].r); } if (ch[0] == 'C') { e[i].id = 3; flag2 = false; scanf("%lld%lld",&e[i].l,&e[i].r); } } if (n <= 1000 && q <= 1000) solve1(); else if (flag1 && flag2) solve2(); else if (flag1) solve2(); else if (flag2) solve3(); else solve4(); return 0; }
3. 路径
问题描述
实在不知道怎么编题面了,就写得直白一点吧。反正没几个人写得完三题,估计都看不到这里。
给出一个仙人掌图,求图中最长路的长度。
Emmm……还是稍微具体一点吧。
仙人掌图是指一个有N个点与M条边的无向图,点从1到N标号,每条边有各自的长度,图中可能存在若干个简单环,但是,每个点最多只会属于1个简单环路。简单环是指一个经过至少两个点、且不经过重复点的环。(这里仙人掌图的定义也许和你在别处见过的不太一样,请仔细审题)
例如,图1所示的是一个仙人掌图,但图2则不是,因为3号点同属于两个简单环。
图1
图2
给出一个仙人掌图,你需要求出图中的最长路的长度。最长路不能经过重复的点。例如,假设图中所有边长度都为1的话,图1中的仙人掌图的一条最长路为1-2-3-4-5-6,长度为5。
输入格式
第一行包含1个整数Q,表示数据组数;
每组数据的第一行2个整数N,M,表示仙人掌图的点数和边数;
每组数据的接下来M行,每行3个正整数x,y,z,描述一条连接点x与点y,长度为z的边。
输出格式
对于每组数据输出一行,每行包含一个整数,表示最长路径的长度。
样例输入
2
6 7
1 2 1
2 3 1
3 1 1
3 4 1
4 5 1
5 6 1
6 4 1
4 4
1 2 1
2 3 2
3 4 3
4 1 4
样例输出
5
9
数据规模
对于10%的数据,Q ≤ 5,n ≤ 10;
另有20%的数据,满足n=m+1;
另有20%的数据,满足n=m;
另有20%的数据,满足每个环上的点数≤ 20;
对于100%的数据,Q ≤ 1000, 所有测试点的n之和 ≤ 100,000,z≤ 1000。
分析:思路同:bzoj1023,只不过边权不再是1,并且也没有了“距离”的概念,那么所有地方直接取max就好了,不必在环上走过的长度取min.环上的部分特别容易写错,需要引起注意.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 300010; int Q,n,m,head[maxn],to[maxn],w[maxn],tot = 1,nextt[maxn]; int deep[maxn],pos[maxn],pre[maxn],low[maxn],dfs_clock; int ans,f[maxn],fa[maxn],cnt,a[maxn],sum[maxn],l,r; void init() { memset(head,0,sizeof(head)); tot = 1; memset(deep,0,sizeof(deep)); memset(pre,0,sizeof(pre)); memset(low,0,sizeof(low)); dfs_clock = 0; ans = 0; memset(f,0,sizeof(f)); memset(fa,0,sizeof(fa)); cnt = 0; l = r = 1; } void add(int x,int y,int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void solve(int x,int y) { memset(a,0,sizeof(a)); memset(pos,0,sizeof(pos)); cnt = deep[y] - deep[x] + 1; for (int i = y; i != x; i = fa[i]) a[cnt--] = i; a[cnt] = x; cnt = deep[y] - deep[x] + 1; for (int i = 1; i <= cnt; i++) { a[cnt + i] = a[i]; for (int j = head[a[i]];j;j = nextt[j]) { int v = to[j]; if (v == a[i + 1]) { sum[i + 1] = w[j]; break; } } } for (int i = 2; i <= cnt; i++) //不能从1复制,因为上面在处理i=cnt的时候已经处理了sum[cnt + 1]. sum[i + cnt] = sum[i]; for (int i = 1; i <= cnt * 2; i++) sum[i] += sum[i - 1]; l = 1,r = 0; for (int i = 1; i <= cnt * 2; i++) { while (l <= r && i - pos[l] >= cnt) l++; ans = max(ans,f[a[pos[l]]] + f[a[i]] + sum[i] - sum[pos[l]]); while (l <= r && f[a[pos[r]]] - sum[pos[r]] <= f[a[i]] - sum[i]) r--; pos[++r] = i; } for (int i = 2; i <= cnt; i++) f[x] = max(f[x],f[a[i]] + max(sum[i],sum[cnt + 1] - sum[i])); } void dfs(int u,int faa) { pre[u] = low[u] = ++dfs_clock; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (v == faa) continue; if (!pre[v]) { deep[v] = deep[u] + 1; fa[v] = u; dfs(v,u); low[u] = min(low[u],low[v]); } else low[u] = min(low[u],pre[v]); if (pre[u] < low[v]) { ans = max(ans,f[u] + f[v] + w[i]); f[u] = max(f[u],f[v] + w[i]); } } for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (v == faa) continue; if (fa[v] != u && pre[u] < pre[v]) solve(u,v); } } int main() { scanf("%d",&Q); while (Q--) { init(); scanf("%d%d",&n,&m); for (int i = 1; i <= m; i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } dfs(1,0); printf("%d\n",ans); } return 0; }
最初的暴力还写错了:
void dfs(int u,int dis) { ans = max(ans,dis); vis[u] = 1; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (!vis[v]) dfs(v,dis + w[i]); } }
如果改成下面这样就对了:
void dfs(int u,int dis) { ans = max(ans,dis); vis[u] = 1; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (!vis[v]) dfs(v,dis + w[i]); } vis[u] = 0; }
vis数组没有清零,u这个点可能会有多条路径经过.最后写急了没有考虑到这一点.
教训:
1.把握好时间,给后面的题目留充足的时间,对于一些难写的题目,要预估写好的时间.
2.不要不敢写正解,有时候正解比暴力还要容易.
3.代码习惯:如果一个地方可能会爆int,那么在内存不是很紧的情况下,所有变量用long long.
4.表达式拆开来写.
5.学会造大数据来对拍,有时候小数据找不出错误来.