Sequence
【题目简述】
Fiugou 想要在一个长度为
N
的序列 A 中找到不同位置的三个数,以这三个数为三边长来构成一个三角形。但是它希望在满足条件下,这三个数的位置尽量靠前。具体地,设这三个数的为
但是这个序列中的数可能会发生变化。所以 Fiugou 给出了
1 x y:将
Ax
改为
y
2:查询最优的合法解,从小到大给出这三个数(而不是位置)。
【输入格式】
第一行一个整数
第二行有
N
个整数,代表初始序列。
第三行一个整数
接下来
M
行操作,两种操作格式如上所述。
【输出格式】
共
【样例输入】
6
7 1 3 4 5 1
3
2
1 3 5
2
【样例输出】
3 5 7
4 5 7
【数据范围】
对于10%的数据,
N<=10,M<=5
对于30%的数据,
N<=100,M<=25
对于50%的数据,
N<=1000,M<=1000
对于100%的数据,
N<=100000,M<=1000
对于100%的数据,
0<=Ai<=109,1<=x<=N,0<=y<=109
Solution :
注意到即使是
Fibnacci
数列在50项之后也超出
109
了,所以
503
枚举一下
Code :
/*************************************************************************
> File Name: sequence.cpp
> Author: Archer
************************************************************************/
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
const int N = 111111;
int n, seq[N], m;
typedef long long ll;
typedef pair<int, int> PII;
#define REP(i, l, r) for (int i = l; i <= r; i++)
#define MP make_pair
inline int read(){
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
inline void setIO(){
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
}
inline bool check(int x, int y, int z){
return (seq[x] + seq[y] > seq[z] && seq[y] + seq[z] > seq[x] && seq[x] + seq[z] > seq[y]);
}
inline void solve(){
REP(i, 3, min(n, 50)) REP(j, 2, i-1) REP(k, 1, j-1)
if (check(i, j, k)){
int _ = min(min(seq[i], seq[j]), seq[k]), __ = max(max(seq[i], seq[j]), seq[k]);
printf("%d %d %d\n", _, seq[i] + seq[j] + seq[k] - _ - __, __);
return;
}
printf("-1 -1 -1\n");
}
int main(){
setIO();
n = read();
REP(i, 1, n) seq[i] = read();
m = read();
while (m--){
int op = read();
if (op == 2) solve();
else{
int x = read(), y = read();
seq[x] = y;
}
}
return 0;
}
登山
【题目简述】
恶梦是一个登山爱好者,今天他来到了黄山。
俗话说的好,不走回头路。所以在黄山,你只能往前走,或者往上走。并且很显然的是,当你走到山脊的时候,你不能够往上走,你只能往前走一步再往上走。
抽象一点而言就是,你可以把黄山视为一个N∗N格点图,恶梦从(0,0)开始出发,要走到(N,N)。当他走到位置(x,y)的时候,它可以往(x+1,y),或(x,y+1)走。并且当他走到(x,x)的时候,由于他已经处在了山脊上,所以他不能够往(x,x+1)方向上走。
当恶梦兴致勃勃准备开始爬山的时候,他的同伴告诉他,黄山由于年久失修,有一些位置出现了大坑,不能走。恶梦觉得更刺激了,但他想先知道他能有多少种方式走到黄山顶。
由于这个数字很大,所以你只需要将答案对109+7取模输出即可。
【输入格式】
第一行包括两个整数N,C,分别表示你可以把黄山视作一个N∗N的格点图,并且黄山上面有C个位置出现了大坑。
接下来的C行,每行包括两个整数X,Y,表示X,Y这个位置不能走。保证X>=Y,也就是说(X,Y)必然在山上。
保证这C个点互不相同。
【输出格式】
输出只有一个整数Ans,表示恶梦爬上山顶的路径数对109+7取模的值。
【数据范围】
对于 30%的数据,
保证N<=5000
对于另外 20%的数据,
保证C=0
对于另外 20%的数据,
保证C=1
对于 100%的数据,保证
N<=100000,C<=1000
保证对于(0,0),(N,N)不存在障碍点。
Solution :
C比较小,这启示我们从障碍点入手。事实上也是这样的。我们将所有的障碍点以及
(N,N)
视为关键点,先将所有的关键点按x坐标从小到大排序,设
Fi
表示我们从
(0,0)
走到第i个关键点,除当前点以外不经过任何关键点的方案数。我们也要用补集的思想,也就是说先计算出会经过其他关键点的方案数,设
Gi
为这个方案数。假设一条这样的路径为
(P1,P2,...Pj,Pi)
,其中
Pj
是我们经过的第一个关键点。我们枚举这个
Pj
,那么
Gi=∑i−1j=1Fj∗WaysPj−>Pi
,
Pj必须能走到Pi
。最后
Fi=Ways(0,0)−>Pi−Gi
Code :
/*************************************************************************
> File Name: walk.cpp
> Author: Archer
************************************************************************/
#include <bits/stdc++.h>
#define fe first
#define se second
using namespace std;
typedef pair<int,int> P;
const int MAXM = 1005,MAXN = 200015,Mo = int(1e9) + 7;
P Block[MAXM];
int Fc[MAXN],Rv[MAXN],F[MAXN],N,C;
int Quick(int a,int b){
if (!b) return 1;
int mid = Quick(a,b >> 1);
if (b & 1) return mid * 1ll * mid % Mo * a % Mo;
return mid * 1ll * mid % Mo;
}
int Getc(int n,int m){
if (m > n) return 0;
return Fc[n] * 1ll * Rv[m] % Mo * Rv[n - m] % Mo;
}
int Normal(int s,int t,int x,int y){
if (x < s || y < t) return 0;
return Getc(x + y - s - t,x - s);
}
int Walk(int s,int t,int x,int y){
return (Normal(s,t,x,y) - Normal(s,t,y - 1,x + 1) + Mo) % Mo;
}
void setIO(){
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
}
int main(){
setIO();
scanf("%d%d", &N, &C);
Fc[0] = 1;
for(int i = 1;i <= N * 2 + 5;i ++) Fc[i] = Fc[i - 1] * 1ll * i % Mo;
Rv[N * 2 + 5] = Quick(Fc[N * 2 + 5], Mo - 2);
for(int i = N * 2 + 5;i;i --) Rv[i - 1] = Rv[i] * 1ll * i % Mo;
for(int i = 1;i <= C;i ++) scanf("%d%d", &Block[i].fe, &Block[i].se);
Block[++ C] = P(N,N);
sort(Block + 1,Block + C + 1);
for(int i = 1;i <= C;i ++)
{
F[i] = Walk(0,0,Block[i].fe,Block[i].se);
for(int j = 1;j < i;j ++)
if (Block[j].se <= Block[i].se)
F[i] = (F[i] - F[j] * 1ll * Walk(Block[j].fe,Block[j].se,Block[i].fe,Block[i].se) % Mo + Mo) % Mo;
}
printf("%d\n", F[C]);
return 0;
}
Melancholy
【题目简述】
DX3906星系,Melancholy星上,我在勘测这里的地质情况。
我把这些天来已探测到的区域分为N组,并用二元组(D,V)对每一组进行标记:其中D为区域的相对距离,V为内部地质元素的相对丰富程度。
在我的日程安排表上有Q项指派的计划。每项计划的形式是类似的,都是“对相对距离D在[L,R]之间的区域进行进一步的勘测,并在其中有次序地挑出K块区域的样本进行研究。”采集这K块的样品后,接下来在实验中,它们的研究价值即为这K块区域地质相对丰富程度V的乘积。
我对这Q项计划都进行了评估:一项计划的评估值P为所有可能选取情况的研究价值之和。
但是由于仪器的原因,在一次勘测中,这其中V最小的区域永远不会被选取。
现在我只想知道这Q项计划的评估值对232取模后的值,特殊地,如果没有K块区域可供选择,评估值为0。
【输入格式】
第一行给出两个整数,区域数N与计划数Q。
第二行给出N个整数,代表每一块区域的相对距离D
。
第三行给出N个整数,代表每一块区域的内部地质元素的相对丰富程度V。
接下来的Q行,每一行3个整数,代表相对距离的限制L,R,以及选取的块数K。
【输出格式】
输出包括Q行,每一行一个整数,代表这项计划的评估值对232取模后的值。
【数据范围】
1,2,3−−K=11<=N,Q<=105
4,5,6−−1<=K<=21<=D,V<=109
7,8−−−1<=K<=31<=L<=R<=109
9,10
−−−1<=K<=6
数据保证所有区域的D与V互不相等。
【样例输入输出】
样例输入
53
54726
14532
671
262
183
样例输出
5
924
【样例解释】
第一次被勘测区域的V值有2,5,而能够被选取只有5。
第二次被勘测区域的V值有1,2,3,4,能够被选取的有2,3,4,评估值为2!∗(2∗3+3∗4+2∗4)=52。
第三次被勘测区域的V值有1,2,3,4,5,能够被选取的有2,3,4,5,评估值为3!∗(2∗3∗4+2∗3∗5+2∗4∗5+3∗4∗5)=924。
Solution :
线段树维护不去掉min的答案,然后容斥原理。
Code :
/*************************************************************************
> File Name: melancholy.cpp
> Author: Archer
************************************************************************/
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
typedef long long ll;
typedef unsigned int UI;
typedef pair<int, int> PII;
#define REP(i, l, r) for (int i = l; i <= r; i++)
#define MP make_pair
const int N = 111111;
int n, q;
struct node{int d, v; } blk[N];
struct Node{
Node *lc, *rc; UI sum[10], mn; int l, r;
void clear() { sum[0] = 1;}
inline void merge(){
REP(i, 1, 6) REP(j, 0, i) sum[i] += lc->sum[j] * rc->sum[i - j];
mn = min(lc->mn, rc->mn);
}
}pool[N << 4], *root, *tail = pool;
inline void setIO(){
freopen("melancholy.in", "r", stdin);
freopen("melancholy.out", "w", stdout);
}
inline int read(){
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
inline void build(Node *(&rt), int l, int r){
rt = tail++; rt->clear(); rt->l = l; rt->r = r;
if (l == r){ rt->mn = rt->sum[1] = blk[l].v; return; }
int mid = l+r >> 1;
build(rt->lc, l, mid); build(rt->rc, mid + 1, r);
rt->merge();
}
inline bool cmp(node x, node y) {return x.d < y.d; }
inline Node *merge(Node *t1, Node *t2){
Node *ret = tail++; ret->lc = t1; ret->rc = t2;
ret->merge(); ret->clear();
return ret;
}
inline Node *query(Node *rt, int l, int r){
if (l <= rt->l && rt->r <= r) { return rt; }
int mid = rt->l+rt->r >> 1;
if (r <= mid) return query(rt->lc, l, r);
else if (l > mid) return query(rt->rc, l, r);
else return merge(query(rt->lc, l, r), query(rt->rc, l, r));
}
inline int find(int x){
int l = 0, r = n + 1;
while (l + 1 < r){
int mid = l+r >> 1;
(blk[mid].d < x ? l : r) = mid;
}
return l;
}
int main(){
setIO();
n = read(); q = read();
REP(i, 1, n) blk[i].d = read();
REP(i, 1, n) blk[i].v = read();
sort(blk + 1, blk + 1 + n, cmp);
build(root, 1, n);
while (q--){
int L = read(), R = read(), k = read();
int l = find(L) + 1, r = find(R + 1);
if (l > r) {printf("0\n"); continue;}
Node *info = query(root, l, r);
info->clear();
UI ans = info->sum[k], t = -info->mn;
REP(i, 1, k) ans += t * info->sum[k - i], t *= -info->mn;
REP(i, 1, k) ans *= i;
printf("%u\n", ans);
}
return 0;
}