The 2018 ACM-ICPC Chinese Collegiate Programming Contest(Ningxia)
A - Maximum Element In A Stack
题意:给出栈的一种进栈和出栈的规则,问栈中的最大值
题解:每次进栈的时候将进栈的值与栈中的最大值比较,将较大的值压入栈
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e6 + 56;
typedef long long ll;
int n, p, q, m;
unsigned int SA, SB, SC;
stack<ll> s;
ll ans = 0;
unsigned int rng61(){
SA ^= SA << 16;
SA ^= SA >> 5;
SA ^= SA << 1;
unsigned int t = SA;
SA = SB;
SB = SC;
SC ^= t ^ SA;
return SC;
}
void gen(){
scanf("%d%d%d%d%u%u%u", &n, &p, &q, &m, &SA, &SB, &SC);
for(int i = 1; i <= n; i++){
if(rng61() % (p + q) < p){
//PUSH(rng61() % m + 1);
ll x = rng61() % m + 1;
//cout << x << endl;
if (!s.empty()) x = max(x, s.top());
s.push(x);
}
else{
if (!s.empty()) s.pop();
else continue;
}
if (!s.empty())ans ^= i * s.top();
}
}
int main()
{
int T;
cin >> T;
for (int t = 1; t <= T; t++){
ans = 0;
while (!s.empty()) s.pop();
printf("Case #%d: ", t);
gen();
cout << ans << '\n';
}
return 0;
}
B - Rolling The Polygon
题意:给一个凸多边形,问多边形旋转一周的过程中,凸多边形中的一点Q经过的路径的长度
题解:计算几何
#include <bits/stdc++.h>
#define sqr(x) ((x)*(x))
using namespace std;
const int maxn = 100;
struct Point{
double x, y;
}p[maxn], Q;
struct Vec{
double x, y;
Vec(){}
Vec (double a, double b){
x = a, y = b;
}
double operator*(Vec a){
return a.x*x+a.y*y;
}
}v[maxn];
const Vec X = {1, 0};
double dis(Point a, Point b){
return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}
double len(Vec a){
return sqrt(sqr(a.x) + sqr(a.y));
}
double ang(Vec a, Vec b){
return acos((a * b) / len(a) / len(b));
}
Vec Mns(Point a, Point b){
return Vec(a.x - b.x, a.y - b.y);
}
int n;
int main()
{
int T;
cin >> T;
for (int t = 1; t <= T; t++){
printf("Case #%d: ", t);
scanf("%d", &n);
for (int i = 0; i < n; i++){
scanf("%lf%lf", &p[i].x, &p[i].y);
}
p[n] = p[0]; p[n+1] = p[1];
int sz= 0;
for (int i = 1; i <= n; i++){
v[i] = Mns(p[i+1], p[i]);
}
v[0] = v[n];
scanf("%lf%lf", &Q.x, &Q.y);
double ans = 0;
for (int i = 1; i <= n; i++){
double sita = ang(v[i], v[i-1]);
double l = dis(Q, p[i]);
ans += l * sita;
}
// cout << ans << endl;
printf("%.3f\n", ans);
}
return 0;
}
C - Caesar Cipher
题意:给出原码与密码的变换规则,要用给定的规则将给定的密码还原,当然加密规则特别简单
题解:水题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100;
char p[maxn],c[maxn],s[maxn];
int n, m;
int main()
{
int T;
cin>>T;
for(int t = 1; t <= T; t++)
{
printf("Case #%d: ", t);
cin >> n >> m;
cin >> p >> c >> s;
int key=(int)p[0]-(int)c[0];
for(int i=0; i<m; i++){
if(s[i]>='a'&&c[i]<='z'){
if(s[i]+key>'z')
s[i]=(s[i]-26+key);
else if(s[i]+key<'a')
s[i]=s[i]+key+26;
else
s[i]=s[i]+key;
}
else
{
if(s[i]+key>'Z')
s[i]=(s[i]-26+key);
else if(s[i]+key<'A')
s[i]=s[i]+key+26;
else
s[i]=s[i]+key;
}
}
printf("%s\n",s);
}
return 0;
}
D - Take Your Seat
题意:两个问题,
第一个问题:飞机上n个人,第i个人要坐在第i个位置,但是第一个人忘了自己的位置,所以他就会随机找一个位置坐下来,被坐的这个人发现自己的位置被坐了就会随机找一个没有被坐的位置坐下,这n个人是按顺序上飞机的,问第n个人坐在第n个位置的概率是多少
第二个问题:如果这n个人不是按顺序上飞机的,第n个人坐在第n个位置的概率是多少
题解:第一问,可以推出
化简一下得 (1)
将n-1带入 (2)
将左边一个移向变成 (3)
(3)-(1) 得
得
容易想到
第二问:可以推出
发现相比第一问就加了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e2 + 10;
int main()
{
int T;
double ans1 = 0,ans2 = 0;
scanf("%d", &T);
for (int t = 1; t <= T; t++){
printf("Case #%d: ", t);
double n, m;
scanf("%lf%lf", &n, &m);
if (n == 1) ans1 = 1.0;
else ans1 = 0.5;
double ans2 = (1.0 + m) / (2.0 * m);
//cout << ans1 << " " << ans2 << endl;
printf("%.6f %.6f\n", ans1, ans2);
}
return 0;
}
E - 2-3-4 Tree
题意:给出2-3-4树的构造方法,然后输出2-3-4树的前序遍历
题解:抄了人家的模板
#include <bits/stdc++.h>
#define FOR(i,s,t) for(int i=(s);i<=(t);i++)
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
int a[maxn];
int cnt,rt;
struct Node
{
int f;
vector<int>d,ch;
void init(int fa,int x)//f是该节点的父亲? x是当前的结点拥有的值?
{
f=fa;
d.clear(),ch.clear();
d.push_back(x);//当前节点拥有多少的所有的值
}
}tr[maxn];
void ins(int p,int x)//(1,x)
{
// cout<<"rt:"<<rt<<endl;
if(tr[p].d.size()==3)//代表有三个元素?? 也就是当前节点插满了不能再插进元素了
{
int f=tr[p].f;//父亲是谁
vector<int>cpy_d,cpy_ch;
cpy_d.swap(tr[p].d),cpy_ch.swap(tr[p].ch);//swap ?? 这时候cop_d 和cpy_ch拥有了ty[p].d和ty[p].ch的内容 而这两个变为空?
if(p==rt)//如果p和rt相等??代表已经是根节点了 上面没有结点
{
//相当于中间结点往上提 0 1 2三个结点 中间的自然是1 变为其它两个节点的父亲
tr[rt=++cnt].init(0,cpy_d[1]);//新的结点 父亲变为0 插入d[1]?
tr[++cnt].init(rt,cpy_d[0]);//新的结点 父亲变为rt 插入d[0]? 相当于成为左儿子
tr[p].init(rt,cpy_d[2]);//原来的结点父亲是rt 只剩下d[2]? 相当于成为右儿子
tr[rt].ch.push_back(cnt),tr[rt].ch.push_back(p);//把这两个结点加入到他们的父亲节点的孩子中
if(cpy_ch.size())//如果当前节点有孩子 自然孩子也需要进行操作
{
//左边两个分到左边 右边两个分到右边 大概是为了平衡吧。。。
tr[cnt].ch.push_back(cpy_ch[0]),tr[cpy_ch[0]].f=cnt;//原来的左儿子自然也就成为现在左儿子的左儿子了
tr[cnt].ch.push_back(cpy_ch[1]),tr[cpy_ch[1]].f=cnt;//为什么把1也归为左儿子的儿子?
tr[p].ch.push_back(cpy_ch[2]),tr[cpy_ch[2]].f=p;//为什么把2 3归为右儿子的儿子?
tr[p].ch.push_back(cpy_ch[3]),tr[cpy_ch[3]].f=p;
// cout<<cpy_ch[0]<<" "<<cpy_ch[1]<<" "<<cpy_ch[2]<<" "<<cpy_ch[3]<<endl;
}
p=rt;//现在的根节点已经变为rt了
}
else//不是根节点 上面有节点
{
tr[p].init(f,cpy_d[0]);//左边结点
tr[++cnt].init(f,cpy_d[2]);//新建右边结点
tr[f].d.push_back(cpy_d[1]);//把中间结点提上去
sort(tr[f].d.begin(),tr[f].d.end());//提上元素之后显然要排序
tr[f].ch.push_back(cnt);//多加了个孩子
for(int i=tr[f].ch.size()-1;i>1;i--)//??为什么要交换? 懂了 因为你要保证孩子也是有序的呀 所以要把新插入的结点换到p之后
{
if(tr[f].ch[i-1]!=p) swap(tr[f].ch[i-1],tr[f].ch[i]);
else break;
}
if(cpy_ch.size())
{
tr[p].ch.push_back(cpy_ch[0]),tr[cpy_ch[0]].f=p;
tr[p].ch.push_back(cpy_ch[1]),tr[cpy_ch[1]].f=p;
tr[cnt].ch.push_back((cpy_ch[2])),tr[cpy_ch[2]].f=cnt;
tr[cnt].ch.push_back(cpy_ch[3]),tr[cpy_ch[3]].f=cnt;
}
p=f;
}
}
// cout<<"*"<<tr[p].d.size()<<endl;
//保证当前节点只有两个元素??错的 会有三个
if(tr[p].ch.size()==0)//没有孩子?? 也就不需要在孩子中进行比较和插入了 直接放进去即可
{
tr[p].d.push_back(x);//作为本节点的元素
sort(tr[p].d.begin(),tr[p].d.end());//你必须保证里面的有序的 所有每次插入元素都需要排序???
}
else//有孩子 插入元素就必须和孩子比较了
{
if(x<tr[p].d[0]) ins(tr[p].ch[0],x);//如果x<第一个元素 那么很显然 肯定要插入到做儿子中 (因为元素是有序的)所以递归左儿子
else if(x>tr[p].d[tr[p].d.size()-1]) ins(tr[p].ch[tr[p].ch.size()-1],x);//如果大于最后一个元素 显然是插到右孩子中的 递归右儿子
else//不比第一个小 不比最后一个大 则肯定插入到中间
{
for(int i=1;i<tr[p].d.size();i++)//遍历中间元素
if(x<tr[p].d[i]) {ins(tr[p].ch[i],x);break;}//小于谁就插入到谁的孩子中
}
}
}
void dfs(int p)
{
for(int i=0;i<tr[p].d.size();i++)
printf("%d%c",tr[p].d[i],i==tr[p].d.size()-1?'\n':' ');
for(int i=0;i<tr[p].ch.size();i++) dfs(tr[p].ch[i]);
}
int main()
{
int T;
cin>>T;
int ca=1;
while(T--)
{
int N;
cin>>N;
for(int i=1;i<=N;i++) cin>>a[i];
cnt=rt=1,tr[rt].init(0,a[1]); //第一个点的父亲为0 拥有元素a[1]
for(int i=2;i<=N;i++) ins(rt,a[i]);//rt不是一直是1吗?
printf("Case #%d:\n", ca++);
dfs(rt);
}
return 0;
}
F - Moving On
题意:给出一个图,每条边有长度,每个城市有危险值,要求一条最短路,使得经过的城市的危险值都小于w
题解:离散化,弗洛伊德,按危险值排序,按危险值由小到大加入城市,计算最短路
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=200 + 2;
int r[maxn];
int dis[maxn][maxn][maxn];
int id[maxn];
bool cmp(int i,int j){
return r[i]<r[j];
}
int main(){
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++){
printf("Case #%d:\n",t);
memset(dis,INF,sizeof(dis));
int N,Q;
scanf("%d%d",&N,&Q);
for(int i=1;i<=N;++i){
id[i]=i;
scanf("%d",&r[i]);
}
sort(id+1,id+N+1,cmp);
for(int i=1;i<=N;++i)
for(int j=1;j<=N;++j)
scanf("%d",&dis[0][i][j]);
for(int k=1;k<=N;++k){
int realk=id[k];
for(int i=1;i<=N;++i)
for(int j=1;j<=N;++j) {
dis[k][i][j]=min(dis[k-1][i][j],dis[k-1][i][realk]+dis[k-1][realk][j]);
}
}
for(int q=1;q<=Q;++q){
int u,v,dan;
scanf("%d%d%d",&u,&v,&dan);
int k=0;
for(int i=1;i<=N;++i)
if(r[id[i]]<=dan) k=i;
printf("%d\n",dis[k][u][v]);
}
}
return 0;
}
G - Factories
题意:给出一颗有权树,每个叶子节点可以建设工厂,问建k个工厂,如何安排使得所有工厂的之间的距离之和最短
题解:树型dp
表示以i为根节点的子树,建设j个工厂的最小距离
#include <bits/stdc++.h>
#define FOR(i,s,t) for(int i=(s);i<=(t);i++)
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 2;
const int maxk = 100 + 2;
const ll inf = 1e12;
const ll INF = 0x3f3f3f3f3f3f3f3f;
vector <pair<int, ll> > edge[maxn];
ll dp[maxn][maxk];
int sz[maxn];
int n, k, rt;
ll miner(ll a, ll b){
return a < b ? a : b;
}
void dfs(int u, int fa){
if (edge[u].size() == 1){
sz[u] = 1;
dp[u][1] = 0ll;
}
dp[u][0] = 0ll;
FOR(i, 0, edge[u].size() - 1){
int v = edge[u][i].first;
ll w = edge[u][i].second;
if (v == fa) continue;
dfs(v, u);
sz[u] += sz[v];
dp[u][k] = miner(dp[u][k], dp[v][k]);
for (int j = min(k, sz[u]); j >= 1; j--){
FOR(l, 1, min(j, sz[v])){
dp[u][j] = miner(dp[u][j], dp[u][j - l] + dp[v][l] +1ll * (ll)w *(ll)l * (ll)(1ll * k - 1ll * l));
}
}
}
}
int main() {
//freopen("G.in", "r", stdin);
//freopen("G.out", "w", stdout);
int T;cin >> T;
FOR(cas, 1, T){
printf("Case #%d: ", cas);
scanf("%d%d",&n, &k);
FOR(i, 1, n) FOR(j, 0, k) dp[i][j] =inf;
FOR(i, 1, n) edge[i].clear();
FOR(i, 1, n) dp[i][0] = 0ll;
FOR(i, 1, n) sz[i] = 0;
FOR(i, 1, n - 1){
int u, v;
ll w;
scanf("%d%d%lld", &u, &v, &w);
//printf("%d\n",w);
edge[u].pb(mp(v, w));
edge[v].pb(mp(u, w));
}
rt = 1;
FOR(i, 1, n){
if (edge[i].size() > 1){
rt = i;
break;
}
}
dfs(rt, -1);
printf("%lld\n", dp[rt][k]);
}
return 0;
}
H - Fight Against Monsters
题意:打怪兽,每个怪兽有生命值和攻击力,每个怪兽在没死之前单位时间都会攻击玩家,玩家每打一个怪兽伤害都是从1开始递增,问玩家最少需要承受多少伤害
题解:贪心(排序)
按排序,Turn是需要打几回合怪兽才会死的回合数
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
struct Monster{
int hp, atk;
}mons[maxn];
int n;
bool cmp(Monster A, Monster B){
ll x1 = ceil((sqrt(1.0 + 8.0 * A.hp) - 1) / 2.0);
ll x2 = ceil((sqrt(1.0 + 8.0 * B.hp) - 1) / 2.0);
if (1ll * A.atk * x2 == 1ll * x1 * B.atk) return A.atk > B.atk;
return 1ll * A.atk * x2 > 1ll * x1 * B.atk;
}
ll sum[maxn];
int main()
{
int T;
cin >> T;
for (int t = 1; t <= T; t++){
printf("Case #%d: ", t);
scanf("%d", &n);
for (int i = 0; i < n; i++){
scanf("%d%d",&mons[i].hp, &mons[i].atk);
}
sort(mons, mons+n, cmp);
ll turn = 0ll, ans = 0ll;
for (int i = 0; i <= n + 1; i++) sum[i] = 0ll;
for (int i = n - 1 ; i >= 0; i--){
sum[i] += sum[i + 1] + (ll)mons[i].atk;
}
int i = 0;
for (i = 0; i < n; i++){
turn = (ll)ceil((sqrt(1.0 + 8.0 * mons[i].hp) - 1) / 2.0);
ans += 1ll* sum[i] * (ll)turn;
// cout << turn << endl;
}
cout << ans <<'\n';
}
return 0;
}
J - Nested Triangles
题意:新定义之中东西,Nested Triangles,就是若干个公用一条边的三角形,除了公用的这条边另外的两条边都不相交或者重合,问最大的Nested Triangles最多有几个三角形
题解:几何+最长上升子序列
按照其中一边的边排序,另一边的边最最长上升子序列
#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define FOR(i,s,t) for(int i=(s);i<=(t);i++)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e5 + 5;
struct Vec{
ll x, y;
Vec(){}
Vec(ll a, ll b){x = a,y = b;}
}PQ, QP;
struct Node{
ll x, y;
Vec operator -(const Node A)const{return Vec(x - A.x, y - A.y);}
}node[maxn],P, Q;
vector<int> id[2];
int pre[maxn], f[maxn], c[maxn];
int n;
ll Cross(Vec A, Vec B){return A.x * B.y - A.y * B.x;}
ll Mult(Vec A, Vec B){return A.x * B.x + A.y * B.y;}
bool isUp(Node x){return Cross(PQ, x - P) > 0;}
int lowbit(int x) {return x&(-x);}
void modify(int x, int val){
for (int i = x; i < maxn; i+= lowbit(i)){
if (x != -1 && (c[i] == -1 || f[val] >= f[c[i]])){
if (c[i] == -1 || f[val] > f[c[i]]) c[i] = val;
//else if (f[x] == f[c[i]] && x < c[i]) c[i] = x;
else if (val < c[i]) c[i] = val;
}
}
return ;
}
int query(int x){
int ret = -1;
for (int i = x; i>0; i-=lowbit(i)){
if (c[i] != -1 && (ret == -1 || f[c[i]] >= f[ret])){
if (ret == - 1|| f[c[i]] > f[ret]) ret = c[i];
//else if (f[c[i]] == f[ret] && c[i] < ret) ret = c[i];
else if (c[i] < ret) ret = c[i] ;
}
}
return ret;
}
bool cmp1(int i, int j){return Cross(node[i] - P, node[j] - P) > 0;}
bool cmp2(int i, int j){return Cross(node[i] - Q, node[j] - Q) < 0;}
int a[maxn], b[maxn];
void print(int x){
int i = x;
while (i != -1){
printf("%d\n", i);
i = pre[i];
}
}
int main(){
int T;cin >> T;
FOR(cas, 1, T){
scanf("%lld%lld%lld%lld", &P.x, &P.y, &Q.x, &Q.y);
PQ = Q - P;
QP = P - Q;
id[0].clear();
id[1].clear();
scanf("%d", &n);
FOR(i , 1, n){
scanf("%lld%lld",&node[i].x, &node[i].y);
if (isUp(node[i])) id[0].pb(i);
else id[1].pb(i);
}
int M[2] = {0};
FOR(i, 0, 1){
sort(id[i].begin(), id[i].end(), cmp1);
for (int j = 0; j < id[i].size(); j++){
if (j == 0) a[id[i][j]] = 1;
else if (cmp1(id[i][j - 1], id[i][j])) a[id[i][j]] = a[id[i][j-1]] + 1;
else a[id[i][j]] = a[id[i][j-1]];
}
sort(id[i].begin(), id[i].end(), cmp2);
for (int j = 0; j < id[i].size(); j++){
if (j == 0) b[id[i][j]] = 1;
else if (cmp2(id[i][j - 1], id[i][j])) b[id[i][j]] = b[id[i][j-1]]+1;
else b[id[i][j]] = b[id[i][j-1]];
}
for (int j = 0; j <= id[i].size() ; j++) c[j] = -1;
for (int j = 0; j < id[i].size() ; j++){
int k = j;
while (k + 1 < id[i].size()&& b[id[i][k]] == b[id[i][k + 1]]) k++;
FOR(p, j , k){
int o = id[i][p];
pre[o] = query(a[o] - 1);
f[o] = pre[o] == -1 ? 1 : f[pre[o]] + 1;
M[i] = max(M[i], f[o]);
}
FOR(p, j ,k){
int o = id[i][p];
modify(a[o], o);
}
j = k;
}
swap(P, Q);
swap(PQ, QP);
}
//FOR(j, 1, n) cout << c[j] << " " ;cout << endl;
printf("Case #%d: %d\n", cas, max(M[1], M[0]));
FOR(j, 1, n){
if (f[j] == max(M[1], M[0])){
print(j);
break;
}
}
}
return 0;
}
L - Continuous Intervals
题意:给出一串数,定义一段区间是连续的表示这段区间排序以后相邻两个数的差不超过1
题解:用MAX表示这段区间的最大值,MIN表示这段区间的最小值,CNT表示这段区间的不同数的种类数;MAX-MIN=CNT-1时,满足条件,MAX-MIN>=CNT-1恒成立。简单化简一下就是一段区间的MAX-MIN-CNT+1=0,则这个区间符合条件。
枚举区间的R,计算有多少个L满足L,R这个区间满足条件,用线段树保存MAX-MIN-CNT-1的最小值及其数量,当枚举一个新的R时,最大值受到影响的区间就是右边第一个比a[L]大的数是a[R]的L,可以用单调栈维护,同理最小值也可以用单调栈维护。CNT受影响的区间就是a[R]上一次出现的位置到R这段区间的L,这段区间的每一个L,构成的区间[L,R]都多了一个新的数,所以CNT多了1,所以MAX-MIN-CNT+1,就少1。可以用map维护上一个a[R]出现的位置,也可以离散化。
#include <bits/stdc++.h>
#define FOR(I,S,T) for(int I=(S);I<=(T);I++)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e5 +10;
int M[maxn << 2], cnt[maxn << 2], tag[maxn << 2];
void gather(int p) {
M[p] = min(M[p << 1], M[p << 1 | 1]);
if (M[p << 1] == M[p << 1 | 1]) cnt[p] = cnt[p << 1] + cnt[p << 1 | 1];
else if (M[p << 1] < M[p << 1 | 1]) cnt[p] = cnt[p << 1];
else cnt[p] = cnt[p << 1 | 1];
}
void push(int p){
if (tag[p]){
tag[p << 1] += tag[p];
tag[p << 1 | 1] += tag[p];
M[p << 1] += tag[p];
M[p << 1 | 1] += tag[p];
tag[p] = 0;
}
}
void build(int p, int l, int r){
tag[p] = 0, cnt[p] = 1;
if (l < r){
int mid = (l + r) / 2;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
gather(p);
}
else M[p] = 0;
}
void modify(int p, int tl, int tr, int l, int r, int v){
if (tl > tr) return ;
if (tl > r || tr < l) return ;
if (l <= tl && tr <= r){
tag[p] += v;
M[p] += v;
return;
}
push(p);
int mid = (tl + tr) >> 1;
modify(p << 1, tl, mid, l, r, v);
modify(p << 1 | 1, mid + 1, tr, l, r, v);
gather(p);
}
int query(int p, int tl, int tr, int l, int r){
if (tl > tr) return 0;
if (tr < l || tl > r) return 0;
if (l <= tl && tr <= r) return M[p] == 0 ? cnt[p] : 0;
push(p);
int mid = (tl + tr) >> 1;
return query(p << 1, tl, mid, l, r) + query(p << 1 | 1, mid + 1, tr, l, r);
}
int a[maxn];
map<int, int> pre;
stack<int> up, down;
int main() {
int T;
cin >> T;
FOR(cas, 1, T){
int n;
ll ans = 0;
scanf("%d", &n);
build(1, 1, n);
while (!up.empty()) up.pop();
while (!down.empty()) down.pop();
pre.clear();
FOR(i,1,n) {
scanf("%d", a + i);
while (!up.empty() && a[up.top()] > a[i]) {
int o = up.top();
up.pop();
if (!up.empty()) modify(1, 1, n, up.top() + 1, o, a[o] - a[i]);
else modify(1, 1, n, 1, o, a[o] - a[i]);
}
up.push(i);
while (!down.empty() && a[down.top()] < a[i]) {
int o = down.top();
down.pop();
if (!down.empty()) modify(1, 1, n, down.top() + 1, o, a[i] - a[o]);
else modify(1, 1, n, 1, o, a[i] - a[o]);
}
down.push(i);
if (pre.find(a[i]) != pre.end()) modify(1, 1, n, pre[a[i]] + 1, i - 1, -1);
else modify(1, 1, n, 1, i - 1, -1);
pre[a[i]] = i;
ans += query(1, 1, n, 1, i);
}
printf("Case #%d: %lld\n", cas, ans);
}
return 0;
}