总结&吐槽
难得的把吐槽放到了最前面.这场比赛用的是一场网络赛,本来是要算校内训练积分的.比赛开始五分钟,我还处于刚刚读完两道题的状态,一看榜,北师大12题位居第一,所有题目的总过题人数已经破百了.后来才知道用了区域赛的原题,于是网络赛变成了手速(网速)赛.最后祝贺北师大的队伍191罚时13题AK位居榜首.
言归正传,一开始我去写C,结果大小写打错没看出来连WA两发.之后队友表示D题有结论让我直接莽,结果又是连WA4发...本来想打的正经一些,至此开始往欢乐赛的方向发展.队友自闭推式子,我去开H.之后一段时间找到了手感,切题很顺利,而且做题速度居然相当靠前(不看罚时).E题发现是一个B-Tree,写了一半写自闭了,交给了队友.这时教练终于忍不住解除了我们的限制,于是欢快地拿下来I和L.最后9题收尾2333.
至于网络赛用原题导致名额无法正常分配的问题,目前还没有出来解决办法.听说又爆出来了银川理工学院的一些黑料,那就静等吃瓜.最后贴张图以示尊敬.
比赛地址
可能过几天会失效
[A]Maximum Element In A Stack
题意
维护一个堆栈,要求支持Push和Pop操作,每次操作结束后输出最大值.
题解
签到题
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n, m, a, b, c, p, q, last_pl[5000005], now_pl;
unsigned int st[5000005], tot, nowmax;
unsigned long long int ans = 0;
unsigned int SA, SB, SC;
unsigned long long int i;
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 PUSH(unsigned int a) {
st[++tot] = a;
if (a > nowmax) {
nowmax = a;
last_pl[tot] = now_pl;
now_pl = tot;
}
ans ^= (i * nowmax);
}
void POP() {
if (tot == 0) {
return ;
}
if (now_pl == tot) {
now_pl = last_pl[tot];
nowmax = st[now_pl];
tot--;
} else tot--;
ans ^= (i * nowmax);
}
void gen() {
scanf("%d%d%d%d%u%u%u", &n, &p, &q, &m, &SA, &SB, &SC);
for (i = 1; i <= n; i++) {
if (rng61()%(p+q) < p) PUSH(rng61()%m+1);
else POP();
}
}
int main(){
int T; cin >> T;
for (int t = 1; t <= T; t++) {
memset(st, 0, sizeof(st));
tot = 0;
ans = 0;
nowmax = now_pl = 0;
gen();
printf("Case #%d: %llu\n", t, ans);
}
return 0;
}
[B]Rolling The Polygon
题意
给出一个凸n边形,按逆时针方向给出所有点的坐标.另外在多边形内部存在着给出的一点Q.一开始第一个点和第二个点之间的边平行放在x轴上,之后让这个多边形进行顺时针旋转,求旋转一周之后,点Q的运动轨迹的长度.
题解
Q的轨迹是一段段圆弧,每次计算出旋转的角度和半径,算出对应的弧长即可.
代码
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
constexpr int MN(100);
constexpr double EPS = 1e-6, INF = 1e22, PI = 3.141592653589793238462643383279502884197169399375105820974944592, RAD = 180 / PI;
struct Po
{
double x, y;
double operator |(const Po & o) const
{
return sqrt((x-o.x) * (x-o.x) + (y-o.y) * (y-o.y));
}
} p[MN], C;
int main()
{
int T;
scanf("%d", &T);
for (int CAS=1; CAS<=T; ++CAS)
{
int n;
scanf("%d", &n);
double sum = 0;
for (int i=1; i<=n; ++i)
scanf("%lf %lf", &p[i].x, &p[i].y);
scanf("%lf %lf", &C.x, &C.y);
for (int i=1; i<=n; ++i)
p[n+i] = p[i];
for (int i=1; i<=n; ++i)
{
double a = p[i] | p[i+1];
double b = p[i+1] | p[i+2];
double c = p[i+2] | p[i];
double r = C | p[i+1];
sum += ((PI - acos((a*a + b*b - c*c) / (2 * b*a))) * r);
}
printf("Case #%d: %.3lf\n", CAS, sum);
}
#ifdef _KEVIN
system("pause");
#endif
return 0;
}
[C]Caesar Cipher
题意
给出三个字符串.第二个字符串是第一个字符串经过凯撒密码的加密规则得到的.第三个字符串也是通过相同规则得到的.求对应的原串.
题解
签到题
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define N 10000
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
int T,j,i,x,n,t,m,f;
char a[N],b[N],c[N];
inline int Abs(int x){return (x<0)?-x:x;}
inline void Swap(int &a,int &b){a^=b^=a^=b;}
inline int Min(int a,int b){return (a<b)?a:b;}
inline int Max(int a,int b){return (a>b)?a:b;}
inline int read(){
int p=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>=48&&c<=57) p=(p<<1)+(p<<3)+c-48,c=getchar();
return p;
}
int main(){
scanf("%d",&T);
for (j=1;j<=T;j++){
scanf("%d%d",&n,&m);
scanf("%s",&a);
scanf("%s",&b);
scanf("%s",&c);
t=(int)(b[0]-a[0]);
if (t<0) t+=26;
printf("Case #%d: ",j);
for (i=0;i<m;i++){
x=c[i]-t;
if (x<65) x=int('Z')+x-65+1;
printf("%c",(char)x);
}
cout<<endl;
}
return 0;
}
[D]Take Your Seat
题意
有n个人要坐飞机.编号从1~n.上飞机的顺序也是编号的顺序.第i个人的票上的座位是i位置.第1个人的票丢掉了,所以他会随机挑选一个座位坐下.当后面的某个人发现自己的座位被占掉了之后,他也会随机选一个座位;否则他将坐到自己票上的位置上.在飞机返回的时候,第1个人的票又丢了.此时有m个人,规则和之前一样,只是上飞机的顺序变成了随机的.求两次登机时,最后一个入座的人,恰好坐到自己票上位置的概率.
题解
设第一,二问中的答案为
f_n,g_n
第一问中,当第一个人坐到1号位时,后面所有人都能坐对;坐到第n号位时,最后一个人肯定不能坐对.当坐到第k位时,前k-1能坐对,k相当于变成了一开始的1.后面的n-k+1个人相当于随机选位置,于是产生了递推关系.则有
f_1=1,f_2=\frac{1}{2},f_n=\frac{\sum_{i=1}^{n-1}f_i}{n}
第二问相当于随机1的登机时间.当1是第k个登机的,则前面k-1个人能坐对.后面的n-k+1个人随机选位置.公式如下:
g_n=\frac{\sum_{i=1}^{n}f_i}{n}
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
double n, m;
int main(){
int T; cin >> T;
for (int t = 1; t <= T; t++) {
cin >> n >> m;
if (n == 1) printf("Case #%d: 1.000000 %.6lf\n", t, 0.5+(0.5/m));
else printf("Case #%d: 0.500000 %.6lf\n", t, 0.5+(0.5/m));
}
return 0;
}
[E]2-3-4 Tree
题意
给出n个数,按顺序插入到最小度数为2的B-Tree中.然后输出先序遍历的结果.
题解
关于B-Tree的理论见《算法导论》第三版 P277-P289
直接模拟建树过程即可.
本来数据结构是归我的,但这实在是太难写了于是交给了擅长指针的队友解决
代码
/*
* If we give,
* all we've got,
* we will make it through.
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <string>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cctype>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <climits>
#define GC getchar()
#define _SN(x) {char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define _SAN(a,n) {auto _i=0,_n=n;for(;_i<_n;++_i)_SN(a[_i])}
#define _SA(a,l,r) {auto _i=l,_r=r;for(;_i<_r;++_i)_SN(a[_i])}
#define _gS(_1, _2, _3, _sc, ...) _sc
#define sc(...) _gS(__VA_ARGS__,_SA,_SAN,_SN, ...)(__VA_ARGS__)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define _F0N(i,n) for(auto i=0;i<(n);++i)
#define _FLR(i,l,r) for(auto i=(l);i<(r);++i)
#define _gF(_1, _2, _3, _F, ...) _F
#define F(...) _gF(__VA_ARGS__,_FLR,_F0N, ...)(__VA_ARGS__)
#define _FD0(i,n) for(auto i=0;i<=(n);++i)
#define _FDL(i,l,r) for(auto i=(l);i<=(r);++i)
#define _gFD(_1, _2, _3, _FD, ...) _FD
#define FD(...) _gFD(__VA_ARGS__,_FDL,_FD0, ...)(__VA_ARGS__)
#define FED(src) for(const auto *p=head[src]; p; p=p->next)
#define FRC(R,C) for(int r=0;r<R;++r)for(int c=0;c<C;++c)
#define OPER1(T,x1,b1) inline bool operator<(const T&_o)const{return x1 b1 _o.x1;}
#define OPER2(T,x1,b1,x2,b2) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&x2 b2 _o.x2;}
#define OPER3(T,x1,b1,x2,b2,x3,b3) inline bool operator<(const T&_o)const{return x1 b1 _o.x1||x1==_o.x1&&(x2 b2 _o.x2||x2==_o.x2&&x3 b3 _o.x3);}
#define IL inline
#define LL long long
#define ULL unsigned LL
#define PC putchar
template<class T>
void UPRT(const T _){if(_>=10)UPRT(_/10);PC(_%10+48);}
#define UPL(_) UPRT(_),PC(10)
#define UPD(_) UPRT(_),PC(32)
template<class T>
void PRT(const T _){if(_<0){PC(45),UPRT<ULL>(-(ULL)_);return;}if(_>=10)PRT(_/10);PC(_%10+48);}
#define PL(_) PRT(_),PC(10)
#define PD(_) PRT(_),PC(32)
#define CON constexpr
#define T_CASE int T;sc(T)for(int CASE=1;CASE<=T;++CASE)
#define Tjj int T;sc(T)while(T--)
#define qjj int q;sc(q)while(q--)
#define cincout std::cin.tie(nullptr),std::cout.tie(nullptr),std::ios::sync_with_stdio(false)
#define EPS 1e-8
#define PI 3.141592653589793238
#define MAX_INT 2147483647
#define MIN_INT -2147483648
#define MAX_LL 9223372036854775807LL
#define MIN_LL -9223372036854775808LL
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3fLL
#define priority_queue priority_queue
#define unordered_ unordered_
using std::cin;
using std::cout;
using std::string;
using std::priority_queue;
using std::pair;
using std::vector;
using std::deque;
using std::set;
using std::map;
using std::unordered_set;
using std::unordered_map;
using std::bitset;
using std::sort;
#define MPFD(k) auto it=mp.find(k)
#define MIN(a, b) ((a)<(b)?(a):(b))
#define MIN3(a, b, c) (MIN(a, MIN(b, c)))
#define MAX(a, b) ((a)>(b)?(a):(b))
#define MAX3(a, b, c) (MAX(a, MAX(b, c)))
#define MAXER(a, b) ({if(a<b)a=b;a;})
#define MINER(a, b) ({if(a>b)a=b;a;})
#define MAXA(bg, ed) ({auto __mv=(bg);for(auto __it=__mv+1,__ed=(ed);__it!=__ed;++__it)if(*__it>*__mv)__mv=__it;__mv;})
#define MINA(bg, ed) ({auto __mv=(bg);for(auto __it=__mv+1,__ed=(ed);__it!=__ed;++__it)if(*__it<*__mv)__mv=__it;__mv;})
#define FIND(bg, ed, k) ({auto __pk=(bg);__pk=nullptr;for(auto __it=(bg),__ed=(ed);__it!=__ed;++__it)if(*__it==k){__pk=__it;break;}__pk;})
#define ABS(a) ((a)>0?(a):-(a))
#define FABS(a) ((a)>0?(a):-(a))
#define log2n(x) (log(x)/0.69314718055995)
#define MHD(p1, p2) ((p1.x>p2.x?p1.x-p2.x:p2.x-p1.x)+(p1.y>p2.y?p1.y-p2.y:p2.y-p1.y))
#define DIST(p1, p2) sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y))
#define PB emplace_back
#define EB emplace_back
#define EBK else break
#define ALL(X) (X).begin(),(X).end()
#define SORT(X) std::sort(ALL(X))
#define SORTD(X) std::sort(ALL(X),std::greater<decltype((X)[0])>())
#define SW(a,b) ({auto _t=a;a=b;b=_t;})
#define mem0(a) memset(a,0,sizeof(a))
#define memf1(a) memset(a,-1,sizeof(a))
#define meminf(a) memset(a,0x3f,sizeof(a))
#define GCD(a,b) ({auto __tp=a,__a=a,__b=b;while(__b){__tp=__a%__b;__a=__b;__b=__tp;}__a;})
CON int MN(1e5);
CON int INVALID_VAL(1e9);
enum VAL_IDX
{
L_VAL, M_VAL, R_VAL, VAL_N
};
enum PTR_IDX
{
P_LL, P_ML, P_MR, P_RR, PTR_N
};
template <typename vint>
class Tree
{
private:
struct Node
{
int vn;
vint val[VAL_N];
Node *son[PTR_N], *fa;
bool empty(void) const {return !vn;}
bool full(void) const {return vn == VAL_N;}
void ins_v(vint v)
{
val[vn++] = v;
if (val[0] > val[1])
SW(val[0], val[1]);
if (val[0] > val[2])
SW(val[0], val[2]);
if (val[1] > val[2])
SW(val[1], val[2]);
}
int get_son_pos(vint v) const
{
int pos;
for (pos=0; pos<vn; ++pos)
if (val[pos] > v)
break;
return pos;
}
} _pool[MN], *pool;
Node _NIL, *NIL, *root;
bool is_leaf(const Node *p) const {return p->son[0]==NIL and p->son[1]==NIL and p->son[2]==NIL and p->son[3]==NIL;}
Node *new_node(const vint v, Node *fa)
{
++pool;
pool->vn = 1;
pool->val[0] = v;
pool->val[1] = pool->val[2] = INVALID_VAL;
for (int i=0; i<PTR_N; ++i)
pool->son[i] = NIL;
pool->fa = fa;
return pool;
}
void ins_first(vint v)
{
root = new_node(v, NIL);
}
Node *push_up(Node *p)
{
Node *fa = p->fa, *left, *right;
if (fa == NIL)
{
fa = root = new_node(p->val[M_VAL], NIL);
left = new_node(p->val[L_VAL], fa);
right = new_node(p->val[R_VAL], fa);
fa->son[0] = left, fa->son[1] = right;
left->son[0] = p->son[0], left->son[1] = p->son[1];
p->son[0]->fa = p->son[1]->fa = left;
right->son[0] = p->son[2], right->son[1] = p->son[3];
p->son[2]->fa = p->son[3]->fa = right;
}
else
{
int pos = fa->get_son_pos(p->val[M_VAL]);
fa->ins_v(p->val[M_VAL]);
left = new_node(p->val[L_VAL], fa);
right = new_node(p->val[R_VAL], fa);
left->son[0] = p->son[0], left->son[1] = p->son[1];
p->son[0]->fa = p->son[1]->fa = left;
right->son[0] = p->son[2], right->son[1] = p->son[3];
p->son[2]->fa = p->son[3]->fa = right;
for (int i=P_RR; i>pos+1; --i)
fa->son[i] = fa->son[i-1];
fa->son[pos] = left, fa->son[pos+1] = right;
}
return fa;
}
void dfs(Node *p) const
{
if (p == NIL)
return;
for (int i=0; i<p->vn; ++i)
printf("%d ", p->val[i]);
puts("");
for (int i=0; i<p->vn+1; ++i)
dfs(p->son[i]);
}
public:
Tree(void) :
pool(_pool),
NIL(&_NIL),
root(NIL)
{
NIL->vn = 0;
for (int i=0; i<VAL_N; ++i)
NIL->val[i] = INVALID_VAL;
for (int i=0; i<PTR_N; ++i)
NIL->son[i] = NIL;
NIL->fa = NIL;
}
void clear()
{
pool = _pool;
root = NIL;
}
void insert(vint v)
{
if (root == NIL)
ins_first(v);
else
{
Node *p = root;
while (not is_leaf(p))
{
if (p->full())
p = push_up(p);
p = p->son[p->get_son_pos(v)];
}
if (p->vn == VAL_N)
{
p = push_up(p);
p = p->son[p->get_son_pos(v)];
}
p->ins_v(v);
}
}
void dfs(void) const
{
dfs(this->root);
}
};
Tree<int> t;
int main()
{
int T;
scanf("%d", &T);
for (int CAS=1; CAS<=T; ++CAS)
{
t.clear();
int n;
scanf("%d", &n);
while (n--)
{
int x;
scanf("%d", &x);
t.insert(x);
}
printf("Case #%d:\n", CAS);
t.dfs();
}
#ifdef _KEVIN
system("pause");
#endif
return 0;
}
[F]Moving On
题意
给出n个点,每个点有一个不安全系数.现在有q个询问,每次询问u到v之间的不经过不安全系数超过w的最短路的长度.
题解
签到题
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
constexpr int MN(222);
constexpr int INF(0x3f3f3f3f);
int G[MN][MN][MN];
struct Node
{
int id, danger;
Node(void) { }
Node(int idd, int dangerr) :
id(idd), danger(dangerr)
{
}
bool operator <(const Node &o) const
{
return danger < o.danger;
}
} a[MN];
#define MIN(a, b) ((a)<(b)?(a):(b))
int main()
{
int T;
scanf("%d", &T);
for (int CAS=1; CAS<=T; ++CAS)
{
memset(G, INF, sizeof(G));
int V, Q;
scanf("%d%d", &V, &Q);
for (int i = 1; i <= V; i++)
{
a[i].id = i;
scanf("%d", &a[i].danger);
}
std::sort(a + 1, a + V + 1);
for (int i = 1; i <= V; i++)
for (int j = 1; j <= V; j++)
scanf("%d", &G[0][i][j]);
for (int k = 1; k <= V; k++)
for (int i = 1; i <= V; i++)
for (int j = 1; j <= V; j++)
G[k][i][j] = MIN(G[k - 1][i][j], G[k - 1][i][a[k].id] + G[k - 1][a[k].id][j]);
printf("Case #%d:\n", CAS);
while (Q--)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
printf("%d\n", G[std::upper_bound(a+1, a+V+1, Node(0, w))-(a+1)][u][v]);
}
}
#ifdef _KEVIN
system("pause");
#endif
return 0;
}
[H]Fight Against Monsters
题意
有n个怪兽,每个怪兽有一个生命值和攻击力.然后玩家需要和怪兽进行回合制战斗,每一回合是怪兽先手,会对玩家造成等同于现在存活怪兽的攻击力总和的伤害;之后由玩家进攻,每次只能攻击一个怪兽.对一个怪兽造成的伤害为k,k是对这个怪兽的累计攻击次数.所以对一个怪兽造成的伤害将会是1,2,3,4...求消灭掉所有怪兽时,玩家承受的伤害的最小值是多少.
题解
和NOIP2013的国王游戏非常像.
首先有个显然的结论,那就是攻击怪兽的时候,每次选定一个怪兽,直到把它打死之后再换一个怪兽攻击.怪兽的血量可以转化为一个攻击次数t,设怪兽的攻击力为a.下面考虑如何安排攻击顺序.
如果假设安排i号怪兽在j号怪兽前面比j在i前面更优(忽略中间的其他怪兽),则有
t_ia_i+t_ia_j+t_ja_j \leq t_ja_j+t_ja_i+t_ia_i
化简得
t_ia_j \leq t_ja_i
所以按这个顺序排序,然后直接计算答案即可.
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define N 100010
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
int i,j,n,T;
LL Ans;
int s[1010],a[N],h[N];
struct Data{
LL x,y;
}t[N];
inline bool cmp(Data a,Data b){return a.x*b.y<b.x*a.y;}
inline int Abs(int x){return (x<0)?-x:x;}
inline void Swap(int &a,int &b){a^=b^=a^=b;}
inline int Min(int a,int b){return (a<b)?a:b;}
inline int Max(int a,int b){return (a>b)?a:b;}
inline int read(){
int p=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>=48&&c<=57) p=(p<<1)+(p<<3)+c-48,c=getchar();
return p;
}
inline void Ready(){
int i=0;
for (i=1;i<=1000;i++) s[i]=i*(i+1)/2;
}
inline int Find(int x){
int low=1,high=1000,mid=0,ans=0;
while (low<=high){
mid=(low+high)>>1;
if (s[mid]>=x) ans=mid,high=mid-1;
else low=mid+1;
}
return ans;
}
inline void Work(){
int i=0;
memset(t,0,sizeof(t));
for (i=1;i<=n;i++){
t[i].x=Find(h[i]); t[i].y=a[i];
}
sort(t+1,t+1+n,cmp);
LL tot=0;
Ans=0;
for (i=1;i<=n;i++){
Ans+=tot*t[i].y;
Ans+=t[i].x*t[i].y;
tot+=t[i].x;
}
}
int main(){
Ready();
scanf("%d",&T);
for (j=1;j<=T;j++){
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d%d",&h[i],&a[i]);
Work();
printf("Case #%d: ",j);
cout<<Ans<<endl;
}
return 0;
}
[I]Bubble Sort
题意
给出一段冒泡排序的伪代码和交换次数的定义.定义一个长度为n的序列是几乎有序的当且仅当其LIS的长度不小于n-1.现在需要找出符合题意的1~n的排列总数,要求这个拍立恩在经过至多k次交换后,可以变成几乎有序的.
题解
有待补充
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#define N 1010
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
LL n,k,i,T,j,MOD,Ans;
LL mi[N],jc[N];
inline int Abs(int x){return (x<0)?-x:x;}
inline void Swap(int &a,int &b){a^=b^=a^=b;}
inline int Min(int a,int b){return (a<b)?a:b;}
inline int Max(int a,int b){return (a>b)?a:b;}
inline LL read(){
LL p=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>=48&&c<=57) p=(p<<1)+(p<<3)+c-48,c=getchar();
return p;
}
inline void Ready(){
LL i=0;
memset(mi,0,sizeof(mi));
memset(jc,0,sizeof(0));
mi[0]=1; jc[0]=1;
for (i=1;i<=n;i++) mi[i]=(mi[i-1]*(LL)(k+1))%MOD;
for (i=1;i<=n;i++) jc[i]=(jc[i-1]*(LL)i)%MOD;
return;
}
inline void Work(){
LL i=0,x=0,cnt=0;
Ans=mi[n-k];
for (i=3;i<=n-k;i++){
x=(mi[n-k-i+1]*(n-i-k+1)%MOD)%MOD;
cnt=(cnt+x)%MOD;
}
for (i=2;i<=n-k;i++){
x=(mi[n-k-1]*(n-i-k+1)%MOD)%MOD;
cnt=(cnt+x)%MOD;
}
Ans=(Ans+cnt)%MOD;
Ans=(Ans*jc[k])%MOD;
return;
}
int main(){
T=read();
for (j=1;j<=T;j++){
printf("Case #%d: ",j);
n=read(); k=read(); MOD=read();
Ans=0;
if (k>=n){
Ans=1;
for (i=1;i<=n;i++) Ans=(Ans*(LL)i)%MOD;
printf("%lld\n",Ans);
continue;
}
Ready(); Work();
printf("%lld\n",Ans);
}
return 0;
}
[L]Continuous Intervals
题意
给出一个长度为n的序列.称一个其中[l,r]的子列是连续的,当且仅当把这段区间的所有数排序后,相邻两项的差至多为1.求有多少子列是连续的.
题解
设一个区间[l,r]内的最大值为max,最小值为min,数字种类数为cnt.当且仅当
max-min-cnt=-1
时,符合题意.
然后考虑用线段树维护以l为左端点的所有区间中max-min-cnt的最小值以及有多少区间符合题意.对于max和min,可以使用单调栈维护.对于cnt,可以考虑每个数的贡献.设a上次出现的位置是
last_a
这次出现的位置是i,则只需要对
[last_a+1,i]
区间进行加1操作即可.因为数据较为离散,然后拿一个map维护就好了.最后需要检查一下最小值是不是-1.具体细节见代码.
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
#define N 100010
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
int i,j,T,n,top,Top,w,ss;
int a[N];
LL Ans=0;
map <int,int> M;
struct Tree{
int l,r,d,lz,sum;
}t[N*4];
struct Data{
int x,y;
}d[N],x[N],ans;
inline int Abs(int x){return (x<0)?-x:x;}
inline void Swap(int &a,int &b){a^=b^=a^=b;}
inline int Min(int a,int b){return (a<b)?a:b;}
inline int Max(int a,int b){return (a>b)?a:b;}
inline int read(){
int p=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>=48&&c<=57) p=(p<<1)+(p<<3)+c-48,c=getchar();
return p;
}
inline void Maketree(int x,int low,int high){
int mid=(low+high)>>1;
t[x].l=low; t[x].r=high;
if (low==high) {t[x].sum=1; return;}
Maketree(l(x),low,mid); Maketree(r(x),mid+1,high);
t[x].sum=t[l(x)].sum+t[r(x)].sum;
}
inline void Push(int x){
int p=t[x].lz;
if (p==0) return;
if (!t[x].l) return;
t[l(x)].d+=p; t[r(x)].d+=p;
t[l(x)].lz+=p; t[r(x)].lz+=p;
t[x].lz=0;
}
inline void Add(int x,int low,int high,int val){
int mid=(t[x].l+t[x].r)>>1;
//cout<<x<<" "<<low<<" "<<high<<endl;
if (low==t[x].l&&high==t[x].r){
t[x].d+=val; t[x].lz+=val;
return;
}
if (t[x].lz!=0) Push(x);
if (high<=mid) Add(l(x),low,high,val);
else if (low>mid) Add(r(x),low,high,val);
else Add(l(x),low,mid,val),Add(r(x),mid+1,high,val);
t[x].d=Min(t[l(x)].d,t[r(x)].d);
if (t[l(x)].d==t[r(x)].d) t[x].sum=t[l(x)].sum+t[r(x)].sum;
else if (t[l(x)].d<t[r(x)].d) t[x].sum=t[l(x)].sum;
else t[x].sum=t[r(x)].sum;
}
inline Data Query(int x,int low,int high){
int mid=(t[x].l+t[x].r)>>1;
Data X,y;
if (t[x].l==low&&t[x].r==high) return (Data){t[x].d,t[x].sum};
if (high<=mid) return Query(l(x),low,high);
else if (low>mid) return Query(r(x),low,high);
else {
X=Query(l(x),low,mid);
y=Query(r(x),mid+1,high);
if (X.x==y.x) return (Data){y.x,X.y+y.y};
if (X.x<y.x) return X; else return y;
}
}
int main(){
scanf("%d",&T);
for (j=1;j<=T;j++){
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&a[i]);
printf("Case #%d: ",j);
memset(t,0,sizeof(t)); memset(d,0,sizeof(d)); memset(x,0,sizeof(x));
top=Top=0; Ans=0; M.clear();
Maketree(1,1,n);
for (i=1;i<=n;i++){
while (top){
if (a[i]<=d[top].x) break;
w=d[top].y;
Add(1,d[top-1].y+1,w,a[i]-d[top].x);
top--;
}
d[++top]=(Data){a[i],i};
while (Top){
if (a[i]>=x[Top].x) break;
w=x[Top].y;
Add(1,x[Top-1].y+1,w,x[Top].x-a[i]);
Top--;
}
x[++Top]=(Data){a[i],i};
Add(1,M[a[i]]+1,i,-1); M[a[i]]=i;
ans=Query(1,1,i);
if (ans.x==-1) Ans+=ans.y;
}
printf("%lld\n",Ans);
}
return 0;
}
本文地址: ACM暑期集训第十二场(Unrated)