9月14日:
luogu P1627 [CQOI2009]中位数
题意:给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
题解:根据中位数的性质,将大于b的数记为1,小于b的数记为-1,区间和为0的奇数序列即符合题意,再计数即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, b, a[N], lsum[N], rsum[N];
int l[N], r[N], minn = N, maxx = 0;
int pos;
ll ans = 0;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int main(){n = read(); b = read();rep(i, 1, n){ a[i] = read();if(a[i] < b) a[i] = -1;if(a[i] == b) a[i] = 0, pos = i;if(a[i] > b) a[i] = 1;}rep(i, pos+1, n) rsum[i] = rsum[i-1] + a[i], minn = min(minn, rsum[i]+n);per(i, pos-1, 1) lsum[i] = lsum[i+1] + a[i], minn = min(minn, lsum[i]+n);l[n] = r[n] = 1;rep(i, pos+1, n) r[rsum[i]+n]++;per(i, pos-1, 1) l[lsum[i]+n]++;maxx = 2*n-minn;rep(i, minn, maxx){ans += (ll)l[i] * r[2*n-i];} printf("%lld\n", ans);return 0;
}
luogu P3407 散步
题意:数轴上有n个人,每秒钟在给定的方向(向东或向西)移动一个距离,当一个人与一个人相遇时两人不再移动,求t秒后,指定m个人所在的位置。
题解:预处理出相遇点,然后二分答案与pos+t进行比较即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
const ll inf = 4557430888798830399;
ll n, t, q;
ll s[N], k = 0;
struct people{ll pos, dic, id; } a[N];
bool mycmp(people a, people b) {return a.pos < b.pos; }
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int main(){n = read(); t = read(); q = read();rep(i, 1, n) a[i].pos = read(), a[i].dic = read(), a[i].id = i;sort(a+1, a+n+1, mycmp);rep(i, 2, n){if(a[i].dic == 2 && a[i-1].dic == 1) s[++k] = (a[i].pos+a[i-1].pos)>>1;} s[0] = -inf, s[k+1] = inf;rep(i, 1, q){ll x = read();ll posx = a[a[x].id].pos, flag = a[a[x].id].dic;ll l = 1, r = k, mid;while(l < r){mid = (l+r)>>1;if(flag == 1){if(s[mid] < posx) l = mid+1;if(s[mid] > posx) r = mid;}if(flag == 2){if(mid == l) mid++;if(s[mid] < posx) l = mid;if(s[mid] > posx) r = mid-1;}}mid = l;if(s[mid] < posx && flag == 2){if(s[mid+1] < posx && s[mid+1] != inf) mid++;}if(s[mid] > posx && flag == 1){if(s[mid-1] > posx && s[mid-1] != inf) mid--; }if(flag == 1){if(s[mid] > posx+t && s[mid] > posx) printf("%lld\n", posx+t);else if(s[mid] < posx) printf("%lld\n", posx+t);else if(s[mid] < posx+t && s[mid] > posx) printf("%lld\n", s[mid]);else printf("%lld\n", s[mid]);}if(flag == 2){if(s[mid] < posx-t && s[mid] < posx) printf("%lld\n", posx-t);else if(s[mid] > posx-t && s[mid] < posx) printf("%lld\n", s[mid]);else if(s[mid] > posx) printf("%lld\n", posx-t); else printf("%lld\n", s[mid]);}}return 0;
}
9月15日:
HLOJ 糖果传递
题意:有n个小朋友坐成一圈,每人有ai个糖果。每人只能给左右两人传递糖果。每人每次传递一个糖果代价为1。
题解:环形均分纸牌,只要做过均分纸牌,稍稍分析一下就可以得出结论。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, a[N], b[N], tot = 0, sum[N];
ll ans, cnt;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) a[i] = read(), tot += a[i];tot /= n;rep(i, 1, n) b[i] = a[i]-tot;rep(i, 1, n) sum[i] = sum[i-1] + b[i];
}
void work(){nth_element(sum+1, sum+(n+1)/2, sum+n+1);cnt = sum[(n+1)/2];rep(i, 1, n) ans += abs(sum[i]-cnt);
}
void print(){printf("%lld\n", ans);
}
int main(){init();work();print();return 0;
}
HLOJ The Pilots Brothers refrigerator
题意:给出4×4共16个门把手,改变一个门把手(打开或关闭)需要同时改变同行同列的门把手,当所有门把手都打开时才能打开门。+代表关,-代表开。
题解:要使一个为'+'的符号变为'-',必须其相应的行和列的操作数为奇数;可以证明,如果'+'位置对应的行和列上每一个位置都进行一次操作,则整个图只有这一'+'位置的符号改变,其余都不会改变.
将所有的行和列的位置都加1后,在将其模2之前,对给定的数组状态,将所有的位置操作其所存的操作数个次数,举例,如果a[i][j]==n,则对(i,j)操作n次,当所有的操作完后,即全为‘-’的数组。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
char s[10][10];
int a[10][10], vis[10][10];
int ansx[N], ansy[N], ans = 0, h = 0;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int main(){rep(i, 1, 4) rep(j, 1, 4) cin >> s[i][j];rep(i, 1, 4) rep(j, 1, 4){if(s[i][j] == '+') {a[i][j] ^= 1;rep(k, 1, 4) a[i][k] ^= 1;rep(k, 1, 4) a[k][j] ^= 1; } }rep(i, 1, 4) rep(j, 1, 4){if(a[i][j]) {ans++;ansx[++h] = i;ansy[h] = j;}}printf("%d\n", ans);rep(i, 1, h) printf("%d %d\n", ansx[i], ansy[i]);return 0;
}
HLOJ 占卜DIV
题意(稍稍有点长):
lyd学会了使用扑克DIY占卜。方法如下:一副去掉大小王的扑克共52张,打乱后均分为13堆,编号1~13,每堆4张,其中第13堆称作“生命牌”,也就是说你有4条命。这里边,4张K被称作死神。
初始状态下,所有的牌背面朝上扣下。
流程如下:
1.抽取生命牌中的最上面一张(第一张)。
2.把这张牌翻开,正面朝上,放到牌上的数字所对应编号的堆的最上边。(例如抽到2,正面朝上放到第2堆牌最上面,又比如抽到J,放到第11堆牌最上边,注意是正面朝上放)
3.从刚放了牌的那一堆最底下(最后一张)抽取一张牌,重复第2步。(例如你上次抽了2,放到了第二堆顶部,现在抽第二堆最后一张发现是8,又放到第8堆顶部.........)
4.在抽牌过程中如果抽到K,则称死了一条命,就扔掉K再从第1步开始。
5.当发现四条命都死了以后,统计现在每堆牌上边正面朝上的牌的数目,只要同一数字的牌出现4张正面朝上的牌(比如4个A),则称“开了一对”,当然4个K是不算的。
6.统计一共开了多少对,开了0对称作"极凶",1~2对为“大凶”,3对为“凶”,4~5对为“小凶”,6对为“中庸”,7~8对“小吉”,9对为“吉”,10~11为“大吉”,12为“满堂开花,极吉”。
题解:清一色的模拟题,按照题意直接模拟,注意一下细节。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int s[14][5], k[14], sum[15];
int ans = 0;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}int main(){rep(i, 1, 13) rep(j, 1, 4) {char ch;cin >> ch; if(ch == 'A') s[i][j] = 1;else if(ch == '0') s[i][j] = 10;else if(ch == 'J') s[i][j] = 11;else if(ch == 'Q') s[i][j] = 12;else if(ch == 'K') s[i][j] = 13;else s[i][j] = (int)(ch-'0');}rep(i, 1, 13) k[i] = 4;rep(i, 1, 4){int x = s[13][i];while (x != 13) {sum[x]++;x = s[x][k[x]--]; }}rep(i, 1, 14) if (sum[i] == 4) ans++;printf("%d\n", ans);return 0;
}
HLOJ solders
题意:这个题目的意思是给你n个士兵在棋盘里的坐标,要你将他们排成连续的一行(即与x轴平行),问你最少要将这些士兵移动多少步。
题解:y坐标取中位数,x坐标转换为右值相等即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, sum = 0, totx = 0, toty = 0;
int x[N], y[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int main(){n = read();rep(i, 1, n) x[i] = read(), y[i] = read();sort(y+1, y+n+1);toty = y[n/2+1];sort(x+1, x+n+1);rep(i, 1, n) x[i] -= (i-1);sort(x+1, x+n+1);totx = x[n/2+1];rep(i, 1, n) sum += abs(totx-x[i]) + abs(toty-y[i]);printf("%d\n", sum);return 0;
}
HLOJ Sumdiv
题意:求A的B次方mod 1e9 + 7 的值。
题解:对A进行质因子分解,然后推出计算公式,等比数列求和后,求个逆元,再累乘取模即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const ll mod = 1000000007;
const int N = 1e6 + 50;
ll a, b, p[N], c[N], k = 0;
ll ans = 1;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void divide(ll x){rep(i, 2, sqrt(x)) if(x % i == 0) {p[++k] = i; c[k] = 0;while(x % i == 0) x /= i, c[k]++;}if(x > 1) p[++k] = x, c[k] = 1;
}
ll pow(ll a, ll b){ll ans = 1;while(b){if(b & 1) ans = (ans*a)%mod;b >>= 1;a = a*a%mod;}return ans;
}
void work(){rep(i, 1, k){ans = ans*( ( pow( p[i], b*c[i]+1 )-1 )%mod * pow( p[i]-1, mod-2 )%mod)%mod;}
}
int main(){a = read(); b = read();divide(a); work();if(a == 0) printf("0\n");else printf("%lld\n", (ans+mod)%mod);return 0;
}
HLOJ 防线
题意:用三个整数S,E 和D 来描述一组防具,即这一组防具布置在防线的S,S + D,S + 2D,...,S + KD(K∈Z,S + KD≤E,S + (K + 1)D>E)位置上。求某个具有奇数个防具的位置。
题解:整体为二分的思想,二分坐标,l为0, r为最远的点,因为最多只有一个奇数,所以每次二分找区间的数字和是不是奇数即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
const ll inf = 9984432123;
ll n, t;
ll e[N],s[N],d[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
ll calc(ll x){ll res = 0;for(int i = 1;i <= n;i++)if(s[i] <= x) res = res+(min(e[i], x)-s[i])/d[i]+1;return res;
}
int main(){t = read();while(t--){n = read();ll l = 0, r;for(int i = 1;i <= n;i++) s[i] = read(), e[i] = read(), d[i] = read(), r = max(r, e[i]);ll ans = 0;while(l <= r){ll mid = (l+r)>>1;if(calc(mid) & 1) r = mid-1, ans = mid;else l = mid+1;}if(!ans) puts("There's no weakness.");else printf("%lld %lld\n", ans, calc(ans)-calc(ans-1));}return 0;
}
HLOJ to the max
题意:给定一个n*n的矩阵,求其最大的子矩阵的数字和。(n <= 100)
题解:大水题,预处理前缀和,直接枚举计算,取最大值即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, a[3010][3010];
ll f[3010][3010], ans = 0;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) rep(j, 1, n) a[i][j] = read();f[1][1] = a[1][1];rep(i, 1, n){ f[i][1] = f[i-1][1] + a[i][1]; rep(j, 2, n){f[i][j] = f[i-1][j] + f[i][j-1] + a[i][j] - f[i-1][j-1];}}
}
void work(){ans = a[1][1];rep(i, 1, n) rep(j, 1, n) rep(p, i, n) rep(q, j, n){ll sum = f[p][q] - f[i-1][q] - f[p][j-1] + f[i-1][j-1];ans = max(ans, sum);}
}
void print(){printf("%lld\n", ans);
}
int main(){init();work();print();return 0;
}
HLOJ Sunscreen
题意:
奶牛们计划着去海滩上享受日光浴。为了避免皮肤被阳光灼伤,所有C(1 <= C <= 2500)头奶牛必须在出门之前在身上抹防晒霜。第i头奶牛适合的最小和最 大的SPF值分别为minSPF_i和maxSPF_i(1 <= minSPF_i <= 1,000; minSPF_i <= maxSPF_i <= 1,000)。如果某头奶牛涂的防晒霜的SPF值过小,那么阳光仍然能 把她的皮肤灼伤;如果防晒霜的SPF值过大,则会使日光浴与躺在屋里睡觉变得 几乎没有差别。为此,奶牛们准备了一大篮子防晒霜,一共L(1 <= L <= 2500)瓶。第i瓶 防晒霜的SPF值为SPF_i(1 <= SPF_i <= 1,000)。瓶子的大小也不一定相同,第i 瓶防晒霜可供cover_i头奶牛使用。当然,每头奶牛只能涂某一个瓶子里的防晒霜 ,而不能把若干个瓶里的混合着用。 请你计算一下,如果使用奶牛们准备的防晒霜,最多有多少奶牛能在不被灼 伤的前提下,享受到日光浴的效果?
简明一点就是:给你若干线段和点,每个点可以与包含这个点的线段匹配,求最大匹配数。
题解:线段按右端点排序,防晒霜按防护值排序,之后进行贪心即可,注意防晒霜的数量。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int k, c, ans = 0;
struct cow{ int l, r; } a[N];
struct node{ int l, r; } b[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
bool cmp1(cow x, cow y){return x.l > y.l;}
bool cmp2(node x, node y){return x.l > y.l;}
int main(){c = read(); k = read();rep(i, 1, c) a[i].l = read(), a[i].r = read();rep(i, 1, k) b[i].l = read(), b[i].r = read();sort(a+1, a+c+1, cmp1);sort(b+1, b+k+1, cmp2);rep(i, 1, c) rep(j, 1, k){if(b[j].l >= a[i].l && b[j].l <= a[i].r && b[j].r) {ans++; b[j].r--;break; }}printf("%d\n", ans);return 0;
}
HLOJ Task
题意:有n个机器,m个任务。每个机器至多能完成一个任务。对于每个机器,有一个最大运行时间xi和等级yi,对于每个任务,也有一个运行时间xj和等级yj。只有当xi>=xj且yi>=yj的时候,机器i才能完成任务j,并获得500xj+2yj金钱。问最多能完成几个任务,当出现多种情况时,输出获得金钱最多的情况。
题解:根据任务去选择机器,任务从小到大排列好,遍历的时候。每次先把所有时间大于该任务的机器按照等级存储下来,同等级的机器是等效的,因为只要能完成任务都是等效的,然后取其中恰好大于等于该任务的等级的机器,正确性可以证明。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, m, maxx = 0, ans1, p[N];
ll ans2;
struct node{ int x, y; } a[N];
struct task{ int x, y;} b[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
bool mycmp1(node x, node y){ return (x.x > y.x) || (x.x == y.x && x.y > y.y ); }
bool mycmp2(task x, task y){ return (x.x > y.x) || (x.x == y.x && x.y > y.y ); }
void init(){n = read(); m = read();rep(i, 1, n) a[i].x = read(), a[i].y = read();rep(i, 1, m) b[i].x = read(), b[i].y = read();
}
void work(){sort(a+1, a+n+1, mycmp1);sort(b+1, b+m+1, mycmp2);int j = 1;rep(i, 1, m){while(j <= n && a[j].x >= b[i].x){p[a[j].y] ++;j++;}rep(k, b[i].y, 100){if(p[k]){p[k] --;ans1++; ans2 += (ll)b[i].x*500 + b[i].y * 2;break;}}}
}
void print(){printf("%d %lld\n", ans1, ans2);
}
int main(){init();work();print();return 0;
}
9月16日
HLOJ 货仓选址
题意:在一条数轴上有N家商店,它们的坐标分别为 A[1]~A[N]。现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。
题解:大水题,先按坐标排序,取中位数为货仓地址,直接计算即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, a[N], ans = 0, pos;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) a[i] = read();sort(a+1, a+n+1);
}
void work(){pos = a[n/2+1];rep(i, 1, n) ans += (ll)abs(a[i] - pos);
}
void print(){printf("%lld\n" , ans);
}
int main(){init();work();print();return 0;
}
HLOJ Corral the Cows
题意:约翰打算建一个围栏来圈养他的奶牛.作为最挑剔的兽类,奶牛们要求这个围栏必须是正方形的,而且围栏里至少要有(1<=C< =500)个草场,来供应她们的午餐. 约翰的土地上共有C<=N<=500)个草场,每个草场在一块1x1的方格内,而且这个方格的 坐标不会超过10000.有时候,会有多个草场在同一个方格内,那他们的坐标就会相同. 告诉约翰,最小的围栏的边长是多少?
题解:考虑将坐标离散化,然后求二维前缀和,由于N<=500,所以离散化后最多也只有1000个点,检验时,我们枚举正方形的左上角,用二分求出它的右下角,然后,判断正方形内是否有大于C的草量。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
const int str = 10000;
struct node{ int x, y; } a[N];
int f[3010][3010];
int n, c, minn = N, maxx = 0, ans = 0;
int px[N], py[N];
int bx[N], by[N], xc, yc;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
bool mycmp(node a, node b){ return a.x < b.x; }
void init(){c = read(), n = read();rep(i, 1, n) a[i].x = read(), a[i].y = read(), px[a[i].x]++, py[a[i].y]++;rep(i, 1, str) {if(px[i]) bx[++xc] = i;px[i] = xc;if(py[i]) by[++yc] = i;py[i] = yc;}rep(i, 1, n) f[px[a[i].x]][py[a[i].y]]++;rep(i, 1, xc) rep(j, 1, yc){f[i][j] += f[i-1][j] + f[i][j-1] - f[i-1][j-1];}
}
bool check(int x){rep(i, px[x], xc) rep(j, py[x], yc){int k = 0, l = 0;if (bx[i] - x >= 0) k = px[bx[i] - x];if (by[j] - x >= 0) l = py[by[j] - x];if (f[i][j] - f[k][j] - f[i][l] + f[k][l] >= c)return 1;}return 0;
}
void work(){int l = 1, r = str;while (l < r) {int mid = (l + r) >> 1;if (check(mid)) r = mid;else l = mid + 1;}ans = l;
}
void print(){printf("%d\n", ans);
}
int main(){init();work();print(); return 0;
}
HLOJ Stall Reservation
题意:给出n个区间,第i个区间表示为[li,ri],询问把这些最少的分组数,使得每组内每个区间相离,1<=N<=50,000。
题解:按左端点排序
现在来考虑第i个区间的决策点,对于能分进的组j,必然是aj<lj,第i个区间才能被分进去,但是选哪一个组,按照朴素的思想,必然是选aj最小的或者最大的,因为是按左端点排序,后面的区间左端点必然增大,于是前面区间能分进的组,后面的区间也能分进,于是无论选哪一个组都无所谓,如果不能选,必然要新开一个组。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
struct cow{ int l, r, id; } a[N];
int n, k = 0, f[N];
priority_queue < pii, vector< pii >, greater< pii > > q;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
bool mycmp(cow a, cow b){ return (a.l < b.l || (a.l == b.l && a.r < b.r)); }
void init(){n = read();rep(i, 1, n) a[i].l = read(), a[i].r = read(), a[i].id = i;
}
void work(){sort(a+1, a+n+1, mycmp);q.push(mp(a[1].r, ++k));f[a[1].id] = k;rep(i, 2, n) {if(a[i].l > q.top().first) {int x = q.top().second;q.pop();f[a[i].id] = x;q.push(mp(a[i].r, x));}else {f[a[i].id] = ++k;q.push(mp(a[i].r, k));}}
}
void print(){printf("%d\n", k);rep(i, 1, n) printf("%d\n", f[i]);
}
int main(){init();work();print();return 0;
}
HLOJ Ultra-QuickSort
题意:给定一个序列,求这个序列的逆序对数。
题解:模板题,直接树状数组求逆序对就莫得了。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, a[N], c[N];
ll lowbit(ll x){ return x&(-x);}
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void add(ll k, ll v){ while(k <= N) c[k] += v, k += lowbit(k); }
ll sum(ll k){ll ans = 0;while(k) ans += c[k], k -= lowbit(k);return ans;
}
void init(){while(~scanf("%lld", &n)){if(!n) break;ll ans = 0;memset(c, 0, sizeof(c));rep(i, 1, n) a[i] = read(); per(i, n, 1){if(a[i] == 0) {ans += (ll)i-1;continue;}ans += (ll)sum(a[i]-1);add(a[i], 1); } printf("%lld\n", ans);}
}
int main(){init();return 0;
}
HLOJ 奇数码问题
题意:在一个n*n的网格中进行,其中n为奇数,1个空格和1~n*n-1这n*n-1个数恰好不重不漏地分布在n*n的网格中。空格移动的规则与八数码游戏相同,实际上,八数码就是一个n=3的奇数码游戏。现在给定两个奇数码游戏的局面,请判断是否存在一种移动空格的方式,使得其中一个局面可以变化到另一个局面。
题解:当此表达式成立时,状态可达:(状态1奇偶性==状态2奇偶性)==(空格距离%2==0)。若两个状态的可相互到达,则有,两个状态的逆序奇偶性相同且空格距离为偶数,或者,逆序奇偶性不同且空格距离为奇数数。否则不能。逆序对用树状数组求。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, a[N], c[N];
int lowbit(int x){ return x&(-x);}
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void add(int k, int v){ while(k <= n) c[k] += v, k += lowbit(k); }
int sum(int k){int ans = 0;while(k) ans += c[k], k -= lowbit(k);return ans;
}
int work(){memset(c, 0, sizeof(c));int ans = 0;rep(i, 1, n){a[i] = read();if(!a[i]) continue;ans += sum(n) - sum(a[i]);add(a[i], 1);}return ans&1;
}
void init(){while(~scanf("%d", &n)){n *= n;if(work() == work()) printf("TAK\n");else printf("NTE\n");}
}
int main(){init();return 0;
}
HLOJ Running Median
题意:你需要写一个程序,读入一个整数序列(在int范围内),每读入奇数个数后输出当前的中位数。
题解:考虑用两个堆来维护,一个大根堆一个小根堆。每次比较两个堆的堆顶,如果不相等就交换堆顶,否则堆顶即为所要求的中位数。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int t, n, m, k;
priority_queue < int, vector<int>, greater<int> > x;
priority_queue < int, vector<int>, less<int> > y;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int main(){t = read();while(t--){m = read(); n = read();printf("%d %d\n", m, n/2+1);k = read();while(x.size()) x.pop();while(y.size()) y.pop();printf("%d ", k);y.push(k);rep(i, 2, n){k = read();if(k > y.top()) x.push(k);else y.push(k);while(abs(x.size()-y.size()) > 1){if(x.size() > y.size()){y.push(x.top());x.pop();}else {x.push(y.top());y.pop();}}if(i & 1){if(y.size() > x.size()) printf("%d ", y.top());else printf("%d ", x.top());}if(i % 20 == 19) printf("\n");}printf("\n");} return 0;
}
HLOJ 七夕祭
题意:有一个会场由N排M列共计N×M个摊点组成。但是小LL只对其中的一部分摊点感兴趣。他预先联系了会场的负责人,希望能够通过恰当地布置会场,使得各行中他感兴趣的摊点数一样多,并且各列中他感兴趣的摊点数也一样多。但是摊点已经随意布置完毕了,如果想满足小LL的要求,唯一的调整方式就是交换两个相邻的摊点。两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上,每一行或每一列的第一个位置和最后一个位置也算作相邻。现在小LL想知道他的两个要求最多能满足多少个。在此前提下,至少需要交换多少次摊点。
题解:类似糖果传递,设 bi 的前缀和为 si。如果从第 k 个位置开始,那么第 i 堆和第 i+1 堆交换的纸牌数就是 |si-sk|。总代价就是|s1-sk|+|s2-sk|+|s3-sk|+……+|sn-sk|。发现什么了?当 sk 是 s1~sn 中位数的时候,上式有最小值!所以把 si 排序后,令 sk=s[(n+1)/2],计算代价即可。时间复杂度 O(nlogn),预计得分 100 分。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, m, t, a[N], b[N];
ll ans = 0;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read(); m = read(); t = read();rep(i, 1, t){int x = read(), y = read();a[x]++, b[y]++; }if((t % n != 0) && (t % m != 0)){puts("impossible");exit(0);}
}
void work(){rep(i, 1, n) a[i] -= t/n;rep(i, 1, m) b[i] -= t/m;if(t % n == 0){rep(i, 2, n) a[i] += a[i-1];sort(a+1, a+n+1);rep(i, 1, n) ans += (ll)abs(a[i] - a[(n+1)/2]);}if(t % m == 0){rep(i, 2, m) b[i] += b[i-1];sort(b+1, b+m+1);rep(i, 1, m) ans += (ll)abs(b[i]-b[(m+1)/2]);}
}
void print(){if(t % n != 0) printf("column ");else {if(t % m != 0) printf("row ");else printf("both ");}printf("%lld\n", ans);
}
int main(){init();work();print();return 0;
}
9月17日
HLOJ Color a Tree
题意:有一棵树,每个节点都有一个代价基值Ci。现在要给每个点染色,第一个染根节点,其余的节点染色的时候其父节点必须已染色。每个节点染色会用掉一个时间单位,每个节点染色的代价是染完此节点时的总时间T*Ci。问染完全部节点所需要的最小代价。
题解:每次找到一个权值最大的节点,如果它是根节点,则首先对它染色,否则的话我们可以得出一个结论,在对它的父亲已经染色的情况下,立刻给它染色是最优的。现在重点讨论第二种情况,当它不是根节点时,我们如果对它父亲染了色,则一定会立刻对它染色,所以可以把它和它父亲合并为同一个节点,它和它父亲的儿子都成为了新节点的儿子,它的父亲的父亲则是新节点的父亲。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, root, a[N], ans;
int head[N], cnt = 0;
struct node{ int to, next, fa, t;double v;
} e[N];
void add(int x, int y){cnt ++; e[cnt].to = y;e[cnt].next = head[x];head[x] = cnt;
}
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int get(){int res = -1;double sum = 0;rep(i, 1, n) {if(i != root && sum < e[i].v){sum = e[i].v;res = i;}}return res;
}
void init(){n = read(); root = read(); rep(i, 1, n) a[i] = read(), e[i].v = a[i], ans += a[i], e[i].t = 1;rep(i, 1, n-1){int xx, yy;xx = read(); yy = read();add(xx, yy);e[yy].fa = xx;}
}
void work(){rep(i, 1, n-1){int u = get();ans += a[u] * e[e[u].fa].t;e[u].v = -1;rep(j, 1, n) if (e[j].fa == u) e[j].fa = e[u].fa;a[e[u].fa] += a[u];e[e[u].fa].t += e[u].t;e[e[u].fa].v = (double) a[e[u].fa] / e[e[u].fa].t;}
}
void print(){printf("%d\n", ans);
}
int main(){init();work();print();return 0;
}
HLOJ 兔子与兔子
题意:很久很久以前,森林里住着一群兔子。有一天,兔子们想要研究自己的 DNA 序列。我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母),然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。
题解:Hash表模板题,用Hash表存储前缀DNA序列。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
char s[N];
int n, q;
unsigned long long f[N], p[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){scanf("%s", s+1); q = read();n = strlen(s+1); p[0] = 1;rep(i, 1, n) {f[i] = f[i-1] * 131 + (s[i]-'a'+1);p[i] = p[i-1] * 131;}
}
void work(){rep(i, 1, q){int l1, l2, r1, r2;l1 = read(); r1 = read(); l2 = read(); r2 = read();if(f[r1] - f[l1-1] * p[r1-l1+1] == f[r2] - f[l2-1] * p[r2-l2+1]){puts("Yes");}else puts("No");}
}
int main(){init();work();return 0;
}
HLOJ 最大子序和
题意:输入一个长度为n的整数序列,从中找出一段不超过M的连续子序列,使得整个序列的和最大。
题解:对序列进行单调队列维护即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, m, a[N], sum[N];
ll q[N], ans = 0;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read(); m = read();rep(i, 1, n) a[i] = read(), sum[i] = sum[i-1] + a[i];
}
void work(){int l = 1, r = 1;q[1] = 0;rep(i, 1, n) {while(l <= r && q[l] < i - m) l++;ans = max(ans, sum[i] - sum[q[l]]);while(l <= r && sum[q[r]] >= sum[i]) r--;q[++r] = i;}
}
void print(){printf("%lld\n", ans);
}
int main(){init(); work();print();return 0;
}
HLOJ 无穷的序列
题意:有一个无穷序列如下:110100100010000100000… 请你找出这个无穷序列中指定位置上的数字。
题解:bitset模板题,对序列用bitset维护。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e7 + 50;
int n, a[N], maxn = 0, k;
bitset <100000000> p;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) a[i] = read(), maxn = max(maxn, a[i]);for(int i = 1;i <= maxn; i += k, k++) p.set(i);
}
void print(){rep(i, 1, n) printf("%d\n", p.test(a[i]));
}
int main(){init(); print();return 0;
}
HLOJ Largest Rectangle in a Histogram
题意:这道题让求直方图中最大的矩形(具体略)。
题解:单调栈模板题,对矩形面积用单调栈维护。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, a[N], s[N], w[N], p = 0;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){while(~scanf("%d", &n)){if(n == 0) break; ll ans = 0;rep(i, 1, n) a[i] = read();a[n+1] = p = 0;rep(i, 1, n+1){if(a[i] > s[p]) s[++p] = a[i], w[p] = 1;else {int width = 0;while (s[p] > a[i]) {width += w[p];ans = max(ans, (ll)width * s[p]);p--;}s[++p] = a[i], w[p] = width + 1;}}printf("%lld\n", ans);}
}
int main(){init();return 0;
}
HLOJ Team Queue
题意:有t个团队的人正在排一个长队。每次新来一个人时,如果他有队友在排队,那么这个新人会插队到最后一个队友的身后。如果没有任何一个队友排队,则他会排到长队的队尾。 输入每个团队中所有队员的编号,要求支持如下3种指令(前两种指令可以穿插进行) 对于每个DEQUEUE指令,输出出队人的编号。
题解:队列维护序列,按照题意描述操作序列就行了。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e4 + 50;
int n, m, v[N*N], p, t;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int main(){while(~scanf("%d", &n)){if(n == 0) break;t++; queue <int> q[N];printf("Scenario #%d\n", t);rep(i, 1, n){m = read();rep(j, 1, m) p = read(), v[p] = i;}char ch[100]; int x;while(~scanf("%s", ch)){if(ch[0] == 'S') break;if(ch[0] == 'E') {x = read();if(q[v[x]].empty()) q[0].push(v[x]);q[v[x]].push(x);}if(ch[0] == 'D'){printf("%d\n",q[q[0].front()].front());q[q[0].front()].pop();if(q[q[0].front()].empty()) q[0].pop();}}printf("\n");}return 0;
}
HLOJ Raid*
题意:给定两组点的坐标,求不在同一组内的点的最小距离。
题解:平面最近点对问题,将同一组内的点的距离赋值为正无穷,分治法求一下平面最近点对即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50, inf = 0x3f3f3f3f;
struct node{ int x, y, id; } a[N], q[N];
int n, t;
double ans;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
double dist(node a, node b){ if(a.id == b.id) return 1e9;return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
}
double MIN(double a, double b){ return a < b ? a : b;}
bool mycmp1(node a, node b){ return a.x < b.x; }
bool mycmp2(node a, node b){ return a.y < b.y; }
double divide(int l, int r){if(l == r) return 1e9;int mid = (l+r)>>1, k = 0;double ans = MIN(divide(l, mid), divide(mid+1, r));rep(i, l, r) if(fabs(a[i].x - a[mid].x) <= ans) q[++k] = a[i];sort(q+1, q+k+1, mycmp2);rep(i, 1, k) rep(j, i+1, k){if(q[j].y - q[i].y <= ans) ans = MIN(ans, dist(q[i], q[j]));} return ans;
}
int main(){t = read();while(t --> 0){n = read();rep(i, 1, (n<<1)){a[i].x = read(), a[i].y = read();a[i].id = (i <= n)?0:1;}sort(a+1, a+(n<<1)+1, mycmp1);ans = divide(1, (n<<1));printf("%.3lf\n", ans);}return 0;
}
HLOJ Snowflake Snow Snowflakes
题意:您可能听说没有两个雪花是相似的。 你的任务是编写一个程序来确定这是否真的如此。 您的程序将读取有关雪花集合的信息,并搜索可能相同的一对。 每个雪花都有六个手臂。 对于每个雪花,您的程序将提供六个臂中每个臂的长度的测量。 任何具有相同长度相应臂的雪花都应该被程序标记为可能相同。
题解:在读入一个雪花的时候把这些情况全部放入哈希表中,如果某次插入的时候发生冲突,则说明存在重复的雪花,并且后面的不需要再处理。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e5 + 50;
int n, tot, mod = 99991, snow[N][6], head[N], next[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
int H(int *a){int sum = 0, mul = 1;rep(i, 0, 5){sum = (sum + a[i]) % mod;mul = (ll)mul * a[i] % mod;}return (sum + mul) % mod;
}
bool equal(int *a, int *b){rep(i, 0, 5) rep(j, 0, 5){bool eq = 1;rep(k, 0, 5) if(a[(i+k)%6] != b[(j+k)%6]) eq = 0;if(eq) return 1;eq = 1;rep(k, 0, 5) if(a[(i+k)%6] != b[(j-k+6)%6]) eq = 0;if(eq) return 1;}return 0;
}
bool insert(int *a){int val = H(a);for(int i = head[val];i;i = next[i]){if(equal(snow[i], a)) return 1;}++tot;memcpy(snow[tot], a, 6 * sizeof(int));next[tot] = head[val];head[val] = tot;return 0;
}
int main(){n = read();rep(i, 1, n){int a[10];rep(j, 0, 5) a[j] = read();if(insert(a)){puts("Twin snowflakes found.");return 0;}}puts("No two snowflakes are alike.");return 0;
}
9月18日
HLOJ 前缀统计
题意:给定N个字符串S1,S2...SN,接下来进行M次询问,每次询问给定一个字符串T,求S1~SN中有多少个字符串是T的前缀。输入字符串的总长度不超过10^6,仅包含小写字母。
题解:Trie树模板题,对于每个询问,在检索过程中累加途径的每个节点的cnt值,就是该询问的答案。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, m, end[N];
int trie[N][26], tot = 1;
char s[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void insert(char* str){int len = strlen(str), p = 1;rep(k, 0, len-1){int ch = str[k] - 'a';if(trie[p][ch] == 0) trie[p][ch] = ++tot;p = trie[p][ch];}end[p]++;
}
int search(char* str){int len = strlen(str), p = 1, res = 0;rep(k, 0, len-1){int ch = s[k] - 'a';if (!trie[p][ch]) break;p = trie[p][ch];res += end[p];}return res;
}
int main(){n = read(); m = read();rep(i, 1, n) scanf("%s", s), insert(s);rep(i, 1, m) {scanf("%s", s);printf("%d\n", search(s));}return 0;
}
HLOJ The XOR Largest Pair
题意:在给定的N个整数A1,A2……AN中选出两个进行xor运算,得到的结果最大是多少?
题解:把每个整数看作长度为32的二进制01串(数值较小时在前边补0),并且把A1~Ai-1对应的32位二进制串插入一棵Trie树即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int a[N];
int trie[N*3][2], tot = 0;
int n, ans = 0;
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void insert(int x){int p = 0;per(k, 31, 0){int ch = (x>>k) & 1;if(!trie[p][ch]) trie[p][ch] = ++tot;p = trie[p][ch]; }
}
int search(int x){int p = 0, res = 0;per(k, 31, 0){int ch = (x>>k) & 1;if(trie[p][ch^1]) p = trie[p][ch^1], res = (res<<1) | 1;else p = trie[p][ch], res = (res<<1);}return res;
}
void init(){n = read();rep(i, 1, n) a[i] = read(), insert(a[i]);rep(i, 1, n) ans = max(ans, search(a[i]));
}
void print(){printf("%d\n", ans);
}
int main(){init();print();return 0;
}
HLOJ 子序列累加和
题意:现在有N个数的数列。现在你定义一个子序列是数列的连续一部分,子序列的值是这个子序列中最大值和最小值之差。给你这N个数,小x想知道所有子序列的值得累加和是多少。
题解:这道题分治应该可以做,不过要学习的是这道题的单调栈做法,就是求出序列的所有max值之和,再求出所有的min值之和,再相减,用单调栈实现序列中每一个数的贡献。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, a[N], s[N], maxl[N], minl[N], maxr[N], minr[N], k = 0;
ll ans = 0;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) a[i] = read();
}
void work(){rep(i, 1, n){while(k != 0 && a[s[k]] < a[i]){maxl[i] += maxl[s[k]] + 1;k--; } s[++k] = i;}k = 0;per(i, n, 1){while(k != 0 && a[s[k]] <= a[i]){maxr[i] += maxr[s[k]] + 1;k--; }s[++k] = i;}k = 0;rep(i, 1, n){while (k != 0 && a[s[k]] > a[i]){minl[i] += minl[s[k]] + 1;k--;}s[++k] = i;}k = 0;per(i, n, 1){while (k != 0 && a[s[k]] >= a[i]){minr[i] += minr[s[k]] + 1;k--;}s[++k] = i;}rep(i, 1, n)ans += ((maxl[i]+1)*(maxr[i]+1) - (minl[i]+1)*(minr[i]+1))*a[i];
}
void print(){printf("%lld\n", ans);
}
int main(){init();work();print();return 0;
}
HLOJ holiday
题意:经过几个月辛勤的工作,FJ决定让奶牛放假。假期可以在1…N天内任意选择一段(需要连续),每一天都有一个享受指数W。但是奶牛的要求非常苛刻,假期不能短于P天,否则奶牛不能得到足够的休息;假期也不能超过Q天,否则奶牛会玩的腻烦。FJ想知道奶牛们能获得的最大享受指数。
题解:单调队列维护符合条件的序列的单调性。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
ll n, P, Q, a[N], sum[N];
ll q[N], ans = -1e9;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read(), P = read(), Q = read();rep(i, 1, n) a[i] = read();rep(i, 1, n) sum[i] = sum[i-1] + a[i];
}
void work(){int l = 1, r = 0;rep(i, P, n){while(l <= r && sum[q[r]] >= sum[i-P]) r--;q[++r] = i-P;while(l <= r && q[l] < i-Q) l++;ans = max(ans, sum[i] - sum[q[l]]);}
}
void print(){printf("%lld\n", ans);
}
int main(){init();work(); print();return 0;
}
9月19日
P3132 [USACO16JAN]愤怒的奶牛Angry Cows
题意:有N个草堆在数轴的不同位置,坐标为x1,x2,….,xn。如果玩家以能量R把奶牛发射到坐标x,就会引爆半径R及以内的的草堆,即坐标范围[x−R,x+R]的草堆都会燃爆,每个被奶牛引爆的草堆又会2次引爆半径R-1及以内的的草堆,2次引爆的草堆又会3次引爆半径R-2及以内的的草堆...直到一次引爆后没有其他草堆被波及或半径为0。
现在只有1头奶牛,能量为R,请计算如果要引爆所有的草堆,最小的R是多少?
题解:用f[i]记录以i为以i为圆心可以向左覆盖前i-1个点的最小半径。 再用g[i]记录以i为圆心可以向右覆盖至第n个点的最小半径,二分答案即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a);i <= (b);i++)
#define per(i, a, b) for(int i = (a);i >= (b);i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
double eps = 0.001;
double a[N], s[N], maxx = 0, pos, posl, posr;
double vis[N], f[N], g[N];
double ans;
int n;
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9'){x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) a[i] = read();sort(a+1, a+n+1);rep(i, 2, n) s[i] = a[i] - a[i-1];f[1] = g[n] = 0;rep(i, 2, n){f[i] = 1e9;int l = 1, r = i;while(l + 1 < r){int mid = (l+r)/2;if(f[mid-1] + 1 < a[i]-a[mid-1]) l = mid;else r = mid;}f[i] = min(max(a[i]-a[l-1], f[l-1] + 1), max(a[i] - a[r-1], f[r-1] + 1));}per(i, n-1, 1){g[i] = 1e9;int l = i, r = n - 1;while(l + 1 < r){int mid = (l+r)/2;if(g[mid+1] + 1 < a[mid+1] - a[i]) r = mid;else l = mid;}g[i] = min(max(a[l+1] - a[i], g[l+1] + 1), max(a[r+1] - a[i], g[r+1] + 1));}
}
bool check(double x){ per(i, n, 1){if(f[i] + 1 <= x){for(int j = i;j <= n && a[j] <= a[i] + 2.0*x; j++)if(g[j] + 1 <= x) return true;break;}}return false;
}
void work(){double l = 1.0, r = a[n]*1.0;while(eps < r-l){double mid = (l+r) / 2;if(check(mid)) r = mid;else l = mid;}ans = l;
}
void print(){printf("%.1lf", ans);
}
int main(){init();work();print();return 0;
}
9月23日
HLOJ Dropping Test
题意:给定你n组ai,bi,让你取出n−k组,使得这n−k组的a之和除以b之和最大.
题解:01分数规划模板题,设答案为x, 则 a[i] - b[i] * x 的前n-k组的累加和必须大于等于0。二分答案即可。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const double eps = 1e-9;
const int N = 1e6 + 50;
int n, k;
double a[N], b[N], v[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
bool mycmp(double x, double y) {return x > y; }
bool check(double x){double res = 0;rep(i, 1, n) v[i] = a[i] - b[i] * x;sort(v+1, v+n+1, mycmp); rep(i, 1, n-k) res += v[i];return res >= 0;
}
void init(){while(~scanf("%d%d", &n, &k)) {if(n == 0) break;double l = 0, r = 0;rep(i, 1, n) scanf("%lf", &a[i]), r += a[i];rep(i, 1, n) scanf("%lf", &b[i]);while(r-l > eps){double mid = (l+r) / 2;if(check(mid)) l = mid;else r = mid;}printf("%.0lf\n", l * 100); }
}
int main(){init();return 0;
}
9月24日
HLOJ 花店橱窗
题意:https://www.luogu.org/problem/P1854
题解:一道比较显然的DP,设F(i,j)表示到(i,j)位置的最优值,状态转移也非常显然,不再叙述,主要是输出方案值得学习,看代码理解。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
int n, m, a[2010][2010], f[2010][2010];
int ans = -1e9, k[N];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init() {n = read(); m = read();rep(i, 1, n) rep(j, 1, m) a[i][j] = read(), f[i][j] = -1e9;
}
void find(int x, int y){if(x <= 0) return ;int p = x;while(f[x][p] != y) p++;k[x] = p;find(x-1, y-a[x][p]);return ;
}
void work() {rep(i, 1, n) rep(j, 1, m) rep(k, i-1, j-1){f[i][j] = max(f[i][j], f[i-1][k] + a[i][j]);}rep(i, n, m) ans = max(ans, f[n][i]);printf("%d\n", ans);find(n, ans);rep(i, 1, n) printf("%d ", k[i]);printf("\n");
}
int main(){init();work();return 0;
}
hnoi2010 合唱队
题意:给定一串序列,问有多少种初始序列经过如题操作可以得到此序列。
题解:设f[i][j][1]为可以排成理想队列中[i,j][区间,且以最后一个排进去是第i人的初始队列种数。
f[i][j][0]为可以排成理想队列中[i,j]区间,且以最后一个排进去是第j人的初始队列种数。
限于篇幅,状态转移部分看代码理解。
#include<bits/stdc++.h>#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)using namespace std;typedef pair<int, int> pii;
typedef double db;
const int N = 1e6 + 50;
const int mod = 19650827;
int n, a[N], f[1010][1010][2];
inline int read(){int x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}return x*f;
}
void init(){n = read();rep(i, 1, n) a[i] = read();rep(i, 1, n) f[i][i][1] = 1;per(l, n, 1) rep(r, l+1, n) {f[l][r][0] = (f[l][r][0] + f[l+1][r][0] * (a[l] < a[l+1]))%mod;f[l][r][0] = (f[l][r][0] + f[l+1][r][1] * (a[l] < a[r]))%mod; f[l][r][1] = (f[l][r][1] + f[l][r-1][0] * (a[r] > a[l]))%mod;f[l][r][1] = (f[l][r][1] + f[l][r-1][1] * (a[r] > a[r-1]))%mod;}printf("%d\n", (f[1][n][1] + f[1][n][0])%mod);
}
int main(){init();return 0;
}