ACM-ICPC 2018 南京赛区网络预赛

A. An Olympian Math Problem

计算\(\sum_{i=1}^{n-1}i\cdot i!(MOD\ n)\)

\(\sum_{i=1}^{n-1}i\cdot i! = \sum_{i=1}^{n-1}[(i+1)!-i!](MOD\ n)=n!-1!(MOD\ n)=n-1\)

#include<bits/stdc++.h>
using namespace std;
int T; long long n;
int main(){
for(cin >> T; T; T--) cin >> n, cout << n - 1 << endl;
return 0;
}

B. The writing on the wall

考虑计算以每个点为右下角有多少个合法的矩形

可以发现符合条件的矩形可以用当前点和另一个代表左上角的点唯一表示出来,并且要求两个点构成的矩形中不存在黑点,那么现在对于每个右下角的点来说,方案数就是其合法左上角点的个数

这个可以用单调栈来做,先计算出每个点的向上最高高度

对于当前这个右下角点\(x\),找到其左边高度高于它的最后一个\(y\),假设高度分别为\(height_x,height_y\),合法左上角点为\(area_x,area_y\),可以得到这样的式子:\(area_x = area_y + (y-x+1)\cdot height_x\)

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
using LL = int_fast64_t;
int n,m,k,height[MAXN][111],area[111],lpos[111];
bool A[MAXN][111];
vector<pair<int,int> > vec;
void solve(int kase){
scanf("%d %d %d",&n,&m,&k);
vec.resize(k);
for(int i = 0; i < k; i++) scanf("%d %d",&vec[i].first, &vec[i].second);
for(auto p : vec) A[p.first][p.second] = true;
for(int j = 1; j <= m; j++) for(int i = 1; i <= n; i++) height[i][j] = (A[i][j]?0:height[i-1][j]+1);
LL ret = 0;
for(int i = 1; i <= n; i++){
stack<int> stk;
for(int j = 1; j <= m; j++){
area[j] = 0; lpos[j] = j;
while(!stk.empty() and height[i][stk.top()]>=height[i][j]){
lpos[j] = lpos[stk.top()];
stk.pop();
}
stk.push(j);
ret += area[j] = height[i][j] * (j - lpos[j] + 1) + area[lpos[j]-1];
}
}
printf("Case #%d: %lld\n",kase,ret);
for(auto p : vec) A[p.first][p.second] = false;
}
int main(){
int T; scanf("%d",&T);
for(int kase = 1; kase <= T; kase++) solve(kase);
return 0;
}

C. GDY

按题意模拟即可

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);}; const int MAXN = 2e4+7;
class Player{
private:
int card[14],cardnum;
public:
explicit Player(){
memset(card,0,sizeof(card));
cardnum = 0;
}
void init(){
memset(card,0,sizeof(card));
cardnum = 0;
}
void Getcard(int ID){
//玩家拿卡
card[ID]++;
cardnum++;
}
bool empty(){ return cardnum==0; }
int put(int c){
if(c==-1){
for(int i = 3; i <= 13; i++){
if(card[i]){
card[i]--; cardnum--;
return i;
}
}
for(int i = 1; i <= 2; i++){
if(card[i]){
card[i]--; cardnum--;
return i;
}
}
}
if(card[c]){
card[c]--; cardnum--;
return c;
}
if(card[2]){
card[2]--; cardnum--;
return 2;
}
return -1;
}
int Calpenalties(){
int tot = 0;
for(int i = 1; i <= 13; i++) tot += i*card[i];
return tot;
}
}player[222];
class Game{
private:
int n,m,cur,skiptime,lastcard;
stack<int> stk;
int Needcard(){
if(lastcard==-1) return -1;
else if(lastcard>=3 and lastcard<=12) return lastcard+1;
else if(lastcard==13) return 1;
else if(lastcard==1) return 2;
else return 0;
}
int Getcardfrompile(){
//拿出下一张卡, 如果没卡返回-1
if(stk.empty()) return -1;
else{
int _ = stk.top();
stk.pop();
return _;
}
}
void Getinitcard(){
//轮流取牌
for(int i = 0; i < n; i++){
for(int j = 0; j < 5; j++){
int card = Getcardfrompile();
if(card==-1) return;
//没有卡了直接返回
player[i].Getcard(card);
}
}
}
void Nextplayer(int &now){ now = (now + 1) % n; }
void Playersgetcard(int now){
for(int i = now, flag = false; i!=now or !flag; Nextplayer(i)){
flag = true;
int card = Getcardfrompile();
if(card==-1) return;
player[i].Getcard(card);
}
}
void Taketurns(){
while(true){
int card = Needcard();
//需要的下一张打出的卡 如果当前是2 c返回0, 当前是-1 返回-1
bool done = false;
// done 判断这次是否出牌了
int putcard = player[cur].put(card);
// -1表示没有出卡牌 否则返回出了的牌
if(putcard!=-1){
done = true;
lastcard = putcard;
}
if(player[cur].empty()) return;
if(done){
if(lastcard!=2){
Nextplayer(cur);
skiptime = 1;
continue;
}
else skiptime = n;
}
else Nextplayer(cur), skiptime++;
if(skiptime==n){
Playersgetcard(cur);
lastcard = -1;
skiptime = 0;
}
}
}
void Showresault(){
for(int i = 0; i < n; i++){
int penalty = player[i].Calpenalties();
if(!penalty) puts("Winner");
else printf("%d\n",penalty);
}
}
public:
void init(){
//读入卡牌栈+初始化玩家
scanf("%d %d",&n,&m);
vector<int> vec(m);
for(int i = 0; i < m; i++) scanf("%d",&vec[i]);
while(!stk.empty()) stk.pop();
for(auto it = vec.rbegin(); it != vec.rend(); it++) stk.push(*it);
for(int i = 0; i < n; i++) player[i].init();
cur = 0; skiptime = 0; lastcard = -1;
}
void start(int kase){
Getinitcard();
Taketurns();
printf("Case #%d:\n",kase);
Showresault();
}
}game; int main(){
int T; scanf("%d",&T);
for(int kase = 1; kase <= T; kase++){
game.init();
game.start(kase);
}
return 0;
}

D. Jerome's House

给出的价值其实就是三角形的面积的两倍

首先要把多边形所有边往内部缩进距离\(r\)

然后问题变成找出多边形内接最大三角形

可以枚举两个点然后三分第三个点来找最大值

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const double eps = 1e-8;
const double PI = acos(-1.);
const int MAXN = 1111;
int sgn( double ta, double tb){
if(fabs(ta-tb)<eps)return 0;
if(ta<tb) return -1;
return 1;
}
//点
class Point{
public: double x, y; Point(){}
Point( double tx, double ty){ x = tx, y = ty;} bool operator < (const Point &_se) const{
return x<_se.x || (x==_se.x && y<_se.y);
}
friend Point operator + (const Point &_st,const Point &_se){
return Point(_st.x + _se.x, _st.y + _se.y);
}
friend Point operator - (const Point &_st,const Point &_se){
return Point(_st.x - _se.x, _st.y - _se.y);
}
double operator ^(const Point &b)const{
return x*b.y - y*b.x;
}
//点位置相同(double类型)
bool operator == (const Point &_off)const{
return sgn(x, _off.x) == 0 && sgn(y, _off.y) == 0;
} }; /****************常用函数***************/
//点乘
double dot(const Point &po,const Point &ps,const Point &pe){
return (ps.x - po.x) * (pe.x - po.x) + (ps.y - po.y) * (pe.y - po.y);
}
//叉乘
double xmult(const Point &po,const Point &ps,const Point &pe){
return (ps.x - po.x) * (pe.y - po.y) - (pe.x - po.x) * (ps.y - po.y);
} class Line{
public: Point s, e;//两点表示,起点[s],终点[e]
double a, b, c;//一般式,ax+by+c=0
double angle;//向量的角度,[-pi,pi] Line(){}
Line( Point ts, Point te):s(ts),e(te){}//get_angle();}
Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){} //排序用
bool operator < (const Line &ta)const{
if(angle!=ta.angle) return angle<ta.angle;
return ((s - ta.s)^(ta.e - ta.s)) < 0;
}
//向量与向量的叉乘
friend double operator / ( const Line &_st, const Line &_se){
return (_st.e.x - _st.s.x) * (_se.e.y - _se.s.y) - (_st.e.y - _st.s.y) * (_se.e.x - _se.s.x);
}
//向量间的点乘
friend double operator *( const Line &_st, const Line &_se){
return (_st.e.x - _st.s.x) * (_se.e.x - _se.s.x) - (_st.e.y - _st.s.y) * (_se.e.y - _se.s.y);
}
//从两点表示转换为一般表示
//a=y2-y1,b=x1-x2,c=x2*y1-x1*y2
bool pton(){
a = e.y - s.y;
b = s.x - e.x;
c = e.x * s.y - e.y * s.x;
return true;
}
//半平面交用
//点在向量左边(右边的小于号改成大于号即可,在对应直线上则加上=号)
friend bool operator < (const Point &_Off, const Line &_Ori){
return (_Ori.e.y - _Ori.s.y) * (_Off.x - _Ori.s.x)
< (_Off.y - _Ori.s.y) * (_Ori.e.x - _Ori.s.x);
}
//求直线或向量的角度
double get_angle( bool isVector = true){
angle = atan2( e.y - s.y, e.x - s.x);
if(!isVector && angle < 0)
angle += PI;
return angle;
} //点在线段或直线上 1:点在直线上 2点在s,e所在矩形内
bool has(const Point &_Off, bool isSegment = false) const{
bool ff = sgn( xmult( s, e, _Off), 0) == 0;
if( !isSegment) return ff;
return ff
&& sgn(_Off.x - min(s.x, e.x), 0) >= 0 && sgn(_Off.x - max(s.x, e.x), 0) <= 0
&& sgn(_Off.y - min(s.y, e.y), 0) >= 0 && sgn(_Off.y - max(s.y, e.y), 0) <= 0;
}
//------------直线和直线(向量)-------------
//向量向左边平移t的距离
Line& moveLine( double t){
Point of;
of = Point( -( e.y - s.y), e.x - s.x);
double dis = sqrt( of.x * of.x + of.y * of.y);
of.x= of.x * t / dis, of.y = of.y * t / dis;
s = s + of, e = e + of;
return *this;
}
//直线重合
static bool equal(const Line &_st,const Line &_se){
return _st.has( _se.e) && _se.has( _st.s);
}
//直线平行
static bool parallel(const Line &_st,const Line &_se){
return sgn( _st / _se, 0) == 0;
}
//两直线(线段)交点
//返回-1代表平行,0代表重合,1代表相交
static bool crossLPt(const Line &_st,const Line &_se, Point &ret){
if(parallel(_st,_se)){
if(Line::equal(_st,_se)) return 0;
return -1;
}
ret = _st.s;
double t = ( Line(_st.s,_se.s) / _se) / ( _st / _se);
ret.x += (_st.e.x - _st.s.x) * t;
ret.y += (_st.e.y - _st.s.y) * t;
return 1;
}
};
class Polygon{
public:
const static int maxpn = 5e4+7;
Point pt[maxpn];//点(顺时针或逆时针)
Line dq[maxpn]; //求半平面交打开注释
int n;//点的个数
int judege( Line &_lx, Line &_ly, Line &_lz){
Point tmp;
Line::crossLPt(_lx,_ly,tmp);
return sgn(xmult(_lz.s,tmp,_lz.e),0);
}
int halfPanelCross(vector<Line> &L){
int i, tn, bot, top, ln = L.size();
for(int i = 0; i < ln; i++)
L[i].get_angle();
sort(L.begin(),L.end());
//平面在向量左边的筛选
for(i = tn = 1; i < ln; i ++)
if(fabs(L[i].angle - L[i - 1].angle) > eps)
L[tn ++] = L[i];
ln = tn, n = 0, bot = 0, top = 1;
dq[0] = L[0], dq[1] = L[1];
for(i = 2; i < ln; i ++){
while(bot < top && judege(dq[top],dq[top-1],L[i]) > 0)
top --;
while(bot < top && judege(dq[bot],dq[bot+1],L[i]) > 0)
bot ++;
dq[++ top] = L[i];
}
while(bot < top && judege(dq[top],dq[top-1],dq[bot]) > 0)
top --;
while(bot < top && judege(dq[bot],dq[bot+1],dq[top]) > 0)
bot ++;
dq[++top] = dq[bot];
for(i = bot; i < top; i ++)
Line::crossLPt(dq[i],dq[i + 1],pt[n++]);
return n;
}
}; //以上是板子
int n,r;
Polygon poly;
vector<Point> vec;
vector<Line> vecl;
double Area(Point &A, Point &B, Point &C){ return fabs(xmult(A,B,C)); }
double maxArea(int m){
double ret = 0;
for(int l = 0; l < m; l++){
for(int r = l + 1; r < m; r++){
Point A = vec[l], B = vec[r];
int L = l, R = r;
while(L<R-1){
int mid = (L+R) >> 1;
int rmid = (mid + R) >> 1;
if(Area(A,B,vec[mid])<Area(A,B,vec[rmid])) L = mid;
else R = rmid;
}
ret = max(ret,max(Area(A,B,vec[L]),Area(A,B,vec[R])));
}
}
return ret;
}
void solve(){
scanf("%d %d",&n,&r);
vec.resize(n);
for(int i = 0; i < n; i++){
int x,y; scanf("%d %d",&x,&y);
vec[i].x = double(x); vec[i].y = double(y);
}
reverse(vec.begin(),vec.end());
vecl.clear();
for(int i = 0; i < n; i++){
Point A = vec[i];
Point B = vec[(i+1)%n];
Line L = Line(A,B);
vecl.push_back(L.moveLine(r));
}
int nd = poly.halfPanelCross(vecl);
vec.resize(2*nd);
for(int i = 0; i < nd; i++) vec[i] = vec[i+nd] = poly.pt[i];
printf("%.6f\n",maxArea(nd));
}
int main(){
int T;
for(scanf("%d",&T); T; T--) solve();
return 0;
}

E. AC Challenge

状压DP

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
LL f[1<<20];
const LL INF = 0x3f3f3f3f3f3f3f3f;
int premsk[20],n;
pair<LL,LL> parameter[20];
int main(){
____();
cin >> n;
for(int i = 0; i < n; i++){
cin >> parameter[i].first >> parameter[i].second;
int num; cin >> num;
while(num--){
int pre; cin >> pre;
pre--; premsk[i] |= (1<<pre);
}
}
memset(f,-0x3f,sizeof(f));
f[0] = 0;
LL ret = 0;
for(int msk = 0; msk < (1<<n); msk++){
if(f[msk]==-INF) continue;
for(int i = 0; i < n; i++){
if(msk&(1<<i) or (msk&premsk[i])!=premsk[i]) continue;
f[msk|(1<<i)] = max(f[msk|(1<<i)],f[msk]+parameter[i].second+parameter[i].first*(__builtin_popcount(msk)+1));
ret = max(ret,f[msk|(1<<i)]);
}
}
cout << ret << endl;
return 0;
}

F. An Easy Problem On The Trees

LCT维护字树大小

大佬题解:蒙特卡洛可以发现询问就是求 (sz[u]-1)/d[u]*2,其中 sz[u] 是联通块大小,d[u] 是度数。然后就是一个很经典的维护子树大小的 lct 了。

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
typedef long long int LL;
const LL MOD = 998244353;
int n,m;
LL qpow(LL a, LL b){
LL ret = 1;
while(b){
if(b&1) ret = ret * a % MOD;
b >>= 1;
a = a * a % MOD;
}
return ret;
}
class Link_cut_tree{
private:
int fa[MAXN],ch[MAXN][2],sz[MAXN],deg[MAXN],rev[MAXN],img[MAXN];
bool isroot(int rt){ return ch[fa[rt]][0]!=rt and ch[fa[rt]][1]!=rt; }
int check(int rt){ return rt == ch[fa[rt]][1]; }
void pushdown(int rt){
if(!rev[rt]) return;
rev[ch[rt][0]] ^= 1;
rev[ch[rt][1]] ^= 1;
swap(ch[rt][0],ch[rt][1]);
rev[rt] ^= 1;
}
void pushdownall(int rt){
if(!isroot(rt)) pushdownall(fa[rt]);
pushdown(rt);
}
void pushup(int rt){ sz[rt] = sz[ch[rt][0]] + sz[ch[rt][1]] + img[rt] + 1; }
void rotate(int rt){
int f = fa[rt], gf = fa[f], d = check(rt);
if(!isroot(f)) ch[gf][check(f)] = rt;
fa[rt] = gf;
ch[f][d] = ch[rt][d^1]; fa[ch[rt][d^1]] = f;
ch[rt][d^1] = f; fa[f] = rt;
pushup(f); pushup(rt);
}
void splay(int rt){
pushdownall(rt);
while(!isroot(rt)){
int f = fa[rt];
if(!isroot(f)){
if(check(rt)==check(f)) rotate(f);
else rotate(rt);
}
rotate(rt);
}
}
public:
explicit Link_cut_tree(){ for(int i = 1; i < MAXN; i++) sz[i] = 1; }
void access(int x){
int c = 0;
while(x){
splay(x);
img[x] += sz[ch[x][1]] - sz[c];
ch[x][1] = c;
pushup(x);
x = fa[c = x];
}
}
void makeroot(int x){
access(x);
splay(x);
rev[x] ^= 1;
}
int findroot(int x){
access(x);
splay(x);
while(ch[x][0]) x = ch[x][0];
splay(x);
return x;
}
bool link(int x, int y){
if(findroot(x)==findroot(y)) return false;
makeroot(x);
makeroot(y);
deg[x]++; deg[y]++;
fa[y] = x;
img[x] += sz[y];
return true;
}
bool cut(int x, int y){
if(findroot(y)!=findroot(x) or x==y) return false;
makeroot(x);
access(y);
splay(y);
pushdown(y);
int rt = ch[y][0];
while(true){
pushdown(rt);
if(ch[rt][1]) rt = ch[rt][1];
else break;
}
splay(rt);
deg[rt]--; deg[y]--;
ch[rt][1] = fa[y] = 0;
pushup(rt);
return true;
}
LL query(int u){
makeroot(u);
pushdown(u);
return 2ll * (sz[u]-1) * qpow(deg[u],MOD-2) % MOD;
}
}lct; int main(){
scanf("%d %d",&n,&m);
for(int i = 1; i < n; i++){
int u, v;
scanf("%d %d",&u,&v);
lct.link(u,v);
}
while(m--){
int op; scanf("%d",&op);
if(op==1){
int u, v;
scanf("%d %d",&u,&v);
if(!lct.link(u,v)) puts("-1");
}
else if(op==2){
int u, v;
scanf("%d %d",&u,&v);
if(!lct.cut(u,v)) puts("-1");
}
else if(op==3){
int u; scanf("%d",&u);
printf("%lld\n",lct.query(u));
}
}
return 0;
}

G. Lpl and Energy-saving Lamps

线段树

先把每个可以换灯的月份算出来

然后每次询问二分找这个月份就好了

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
const int INF= 0x3f3f3f3f;
int n,m,A[MAXN];
vector<pair<int,pair<int,int>>> vec;
class SegmentTree{
private:
int l[MAXN<<2], r[MAXN<<2], minn[MAXN<<2];
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
#define pushup(rt) minn[rt] = min(minn[ls(rt)],minn[rs(rt)])
public:
void build(int L, int R, int rt = 1){
l[rt] = L; r[rt] = R;
if(l[rt]+1==R){
minn[rt] = A[L];
return;
}
int mid = (L+R) >> 1;
build(L,mid,ls(rt)); build(mid,R,rs(rt));
pushup(rt);
}
void update(int pos ,int x, int rt = 1){
if(l[rt]+1==r[rt]){
minn[rt] = x;
return;
}
int mid = (l[rt] + r[rt]) >> 1;
if(pos<mid) update(pos,x,ls(rt));
else update(pos,x,rs(rt));
pushup(rt);
}
int qmin(){ return minn[1]; }
pair<int,int> findlmin(int x, int rt = 1){
if(l[rt]+1==r[rt]) return make_pair(l[rt],minn[rt]);
if(minn[ls(rt)]<=x) return findlmin(x,ls(rt));
else return findlmin(x,rs(rt));
}
}ST;
void solve(){
int mth; scanf("%d",&mth);
int p = lower_bound(vec.begin(),vec.end(),make_pair(mth,make_pair(INF,INF))) - vec.begin() - 1;
if(p==(int)vec.size()-1){
printf("%d %d\n",vec[p].second.first,vec[p].second.second);
return;
}
printf("%d %d\n",vec[p].second.first,vec[p].second.second+(mth-vec[p].first)*m);
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",A+i);
ST.build(1,n+1);
vec.push_back(make_pair(0,make_pair(0,0)));
while(ST.qmin()!=INF){
int minn = ST.qmin();
int month = (minn-vec.back().second.second) / m + ((minn-vec.back().second.second)%m==0?0:1);
int tot = vec.back().second.second + month * m;
int num = 0;
while(ST.qmin()<=tot){
auto p = ST.findlmin(tot);
int pos = p.first, val = p.second;
tot -= val;
ST.update(pos,INF);
num++;
}
vec.push_back(make_pair(vec.back().first+month,make_pair(vec.back().second.first+num,tot)));
}
int q; scanf("%d",&q);
while(q--) solve();
return 0;
}

H. Set

01字典树,合并

考虑第一个操作,可以用并查集来做

第二个操作其实就是在字典树中交换左右儿子,然后不断向\(0\)子树递归,这个可以用\(lazy\)标记来优化一下

第三个操作其实就是01字典树从根向下\(k\)的深度每一位和\(x\)相同的数量,统计一下就好了

有点卡常

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 6e5+7;
int n,m,root[MAXN],tot,sum[MAXN<<5],ch[MAXN<<5][2],lazy[MAXN<<5];
int findx(int x){ return x==root[x]?x:root[x]=findx(root[x]); }
#define swap(u, v) u^=v^=u^=v;
inline void pushdown(int rt){
if(lazy[rt]&1){
swap(ch[rt][0],ch[rt][1]);
lazy[ch[rt][0]]++;
}
lazy[ch[rt][0]] += lazy[rt] >> 1;
lazy[ch[rt][1]] += lazy[rt] >> 1;
lazy[rt] = 0;
}
inline void insert(int rt, int x){
for(int i = 0; i < 30; i++){
sum[rt]++;
int bit = (x & (1<<i))?1:0;
if(!ch[rt][bit]) ch[rt][bit] = ++tot;
rt = ch[rt][bit];
}
sum[rt]++;
}
int merge(int u, int v){
if(!u or !v) return u^v;
if(lazy[u]) pushdown(u);
if(lazy[v]) pushdown(v);
sum[u] += sum[v];
ch[u][0] = merge(ch[u][0],ch[v][0]);
ch[u][1] = merge(ch[u][1],ch[v][1]);
return u;
}
inline int query(int rt, int x, int k){
for(int i = 0; i < k; i++){
if(lazy[rt]) pushdown(rt);
int bit = (x&(1<<i))?1:0;
if(!ch[rt][bit]) return 0;
rt = ch[rt][bit];
}
return sum[rt];
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c!='-'&&(c<'0'||c>'9')) c = getchar();
if(c=='-') f = -1,c = getchar();
while(c>='0'&&c<='9') x = x*10+c-'0', c = getchar();
return f*x;
} int main(){
n = read(); m = read();
for(int i = 1; i <= n; i++) root[i] = i;
tot = n;
for(int i = 1; i <= n; i++){
int x = read();
insert(i,x);
}
while(m--){
int op = read();
if(op==1){
int u = read(), v = read();
int fu = findx(u), fv = findx(v);
if(fu==fv) continue;
root[fv] = merge(fu,fv);
}
else if(op==2){
int u = read();
lazy[findx(u)]++;
}
else if(op==3){
int u = read(), k = read(), x = read();
printf("%d\n",query(findx(u),x,k));
}
}
return 0;
}

I. Skr

有个结论,本质不同的回文子串数量不会超过字符串长度

Solution1: 可以先马拉车跑出来,然后对于每个回文中点从最长的子串开始找,直到这个回文串出现过停止

计算总和可以用类似哈希的前缀和

直接用\(set\)判重会T

这里用拉链法来处理冲突

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e6+7;
typedef long long int LL;
const int base = 231LL;
const int MOD = 1e9+7;
const int hashmod = 5e6+7;
char s[MAXN],str[MAXN<<1];
int n,m,len[MAXN<<1],head[hashmod],nxt[MAXN],w[MAXN],tot;
int hax[MAXN],powbase[MAXN];
int pre[MAXN],powt[MAXN]; void manacher(){
int st = 1;
len[0] = 0; len[1] = 1;
for(int i = 2; i < m; i++){
int lp = 2 * st - i;
if(st+len[st]>i+len[lp]) len[i] = len[lp];
else{
int j = st + len[st] - i;
if(j<0) j = 0;
while(i-j>=0 and i+j<m and str[i-j]==str[i+j]) j++;
len[i] = j - 1;
st = i;
}
}
} void preprocess(){
hax[0] = s[0]; powbase[0] = 1;
powt[0] = 1; pre[0] = s[0] - '0';
for(int i = 1; i < n; i++){
hax[i] = (1ll * base * hax[i-1] + s[i]) % hashmod;
powbase[i] = 1ll * powbase[i-1] * base % hashmod;
pre[i] = (pre[i-1] * 10ll + s[i] - '0') % MOD;
powt[i] = powt[i-1] * 10ll % MOD;
}
}
int calhash(int L, int R){
if(!L) return hax[R];
int tp = (hax[R] - 1ll * hax[L-1] * powbase[R-L+1]) % hashmod + hashmod;
return tp>=hashmod?tp-hashmod:tp;
}
int calval(int L, int R){
if(!L) return pre[R];
int tp = (pre[R] - 1ll * pre[L-1] * powt[R-L+1]) % MOD + MOD;
return tp>=MOD?tp-MOD:tp;
} bool exist(int val, int L, int R){
int hashval = calhash(L,R);
for(int i = head[hashval]; i; i = nxt[i]) if(w[i]==val) return true;
tot++;
w[tot] = val; nxt[tot] = head[hashval];
head[hashval] = tot;
return false;
}
int main(){
scanf("%s",s);
n = strlen(s);
m = 0;
for(int i = 0; i < n; i++){
str[m++] = '#';
str[m++] = s[i];
}
str[m++] = '#'; str[m] = '\0';
manacher();
preprocess();
int ret = 0;
for(int i = 0; i < m; i++){
int lp, rp;
if(i&1) lp = i / 2 - len[i] / 2, rp = i / 2 + len[i] / 2;
else lp = i / 2 - len[i] / 2, rp = i / 2 - 1 + len[i] / 2;
while(lp<=rp){
int val = calval(lp,rp);
if(exist(val,lp,rp)) break;
ret = (ret + val) % MOD;
lp++, rp--;
}
}
printf("%d\n",ret);
return 0;
}

Solution2: 当然也能用PAM来做,而且跑得飞快,因为PAM的每个节点上都表示一个不同的回文,所以只要记下这个回文出现的一个位置和长度就可以了

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e6+7;
const int MOD = 1e9+7;
int pref[MAXN],powt[MAXN],n;
char s[MAXN];
int calval(int L, int R){ return (0ll + pref[R] + MOD - 1ll * pref[L-1] * powt[R-L+1] % MOD) % MOD; }
class PAM{
private:
int tot,last,ch[MAXN][10],fail[MAXN],len[MAXN],rpos[MAXN];
public:
PAM(){
len[0] = 0; len[1] = -1;
fail[0] = 1; fail[1] = 0;
tot = 1;
}
int getfail(int x, int pos){
while(s[pos]!=s[pos-len[x]-1]) x = fail[x];
return x;
}
void insert(int pos){
int c = s[pos] - '0';
int u = getfail(last,pos);
if(!ch[u][c]){
len[++tot] = len[u] + 2;
fail[tot] = ch[getfail(fail[u],pos)][c];
rpos[tot] = pos;
ch[u][c] = tot;
}
last = ch[u][c];
}
int solve(){
int ret = 0;
for(int i = 2; i <= tot; i++) ret = (ret + calval(rpos[i]-len[i]+1,rpos[i])) % MOD;
return ret;
}
}pam; int main(){
scanf("%s",s+1);
n = strlen(s+1);
powt[0] = 1;
for(int i = 1; i <= n; i++){
powt[i] = (powt[i-1] * 10ll) % MOD;
pref[i] = (pref[i-1] * 10ll + s[i] - '0') % MOD;
pam.insert(i);
}
printf("%d\n",pam.solve());
return 0;
}

J. Sum

\(f(x)\)的计算方法如下:

把\(x\)分解质因数,如果某个素因子出现了三次及以上那么\(f(x)=0\),否则\(f(x)\)就等于\(2^{(只出现一次的素因子数量)}\),这个可以用欧拉筛来处理出来,然后求前缀和,每次查询复杂度为\(O(1)\)

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e7+7;
using LL = int_fast64_t;
bool npm[MAXN];
int base1[MAXN],pw[MAXN];
LL sum[MAXN];
vector<int> prime;
void preprocess(){
pw[1] = 0;
for(int i = 2; i < MAXN; i++){
if(!npm[i]) prime.push_back(i), pw[i] = 1;
for(int j = 0; ; j++){
if(i*prime[j]>=MAXN) break;
npm[i*prime[j]] = true;
if(base1[i]<2) pw[i*prime[j]] = pw[i] + 1, base1[i*prime[j]] = 0;
else pw[i*prime[j]] = -1, base1[i*prime[j]] = 2;
if(i%prime[j]==0){
if(base1[i]) pw[i*prime[j]] = -1, base1[i*prime[j]] = 2;
else base1[i*prime[j]]++, pw[i*prime[j]] -= 2;
break;
}
}
}
for(int i = 1; i < MAXN; i++) sum[i] = sum[i-1] + (pw[i]==-1?0:(1ll<<pw[i]));
}
int main(){
int T,n;
preprocess();
for(cin >> T; T; T--) cin >> n, cout << sum[n] << endl;
return 0;
}

K. The Great Nim Game

Solution1: 线性基

Nim游戏中如果先手要获胜必须是所有石堆数量异或和不为\(0\)

现在问题转化为给出\(n\)堆石子,要求找到异或和不为\(0\)的子集数

可以先算异或和为\(0\)的子集数,然后再用总的去减掉

因为\(k\le 2^{12}\),所以出现的不同的数的数量不会超过\(4096\)

我们把每个数表示成二进制,然后取他们的一个线性基,假设这个线性基的秩是\(r\),答案就是\(2^n-2^{n-r}\),

可以这样想,因为这\(r\)个基底可以表示出所有\(n\)个数能表示出来的数,所以如果任意取不是基底的数,总能取出几个基底使总的异或和为\(0\),所以方案数就是非基底的数的任意组合方案数

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const LL MOD = 1e9+7;
const int MAXN = 1e7+7;
char n[MAXN];
LL x1,a,b,c,d,e;
int k,base[13],len;
LL qpow(LL x, LL y){
LL ret = 1;
while(y){
if(y&1) ret = ret * x % MOD;
y >>= 1;
x = x * x % MOD;
}
return ret;
}
int f(LL x){ return (a*x*x*x*x+b*x*x*x+c*x*x+d*x+e-1) % k + 1; } int main(){
scanf("%s %lld %lld %lld %lld %lld %lld %d",n,&x1,&a,&b,&c,&d,&e,&k);
int num = k;
len = strlen(n);
if(len<6){
int tp = 0;
for(int i = 0; i < len ; i++) tp = tp * 10 + n[i] - '0';
num = min(num,tp);
}
set<int> S;
int Rank = 0;
for(int i = 1; i <= num; i++, x1 = f(x1)){
if(S.count(x1)) break;
S.insert(x1);
int msk = x1;
for(int i = 12; i >= 0; i--){
if(!(msk&(1<<i))) continue;
if(!base[i]){
base[i] = msk;
Rank++;
break;
}
msk = msk ^ base[i];
}
}
LL pw = 0;
for(int i = 0; i < len; i++) pw = (pw * 10 + n[i] - '0') % (MOD - 1);
LL pn = qpow(2,pw);
printf("%lld\n",(pn-pn*qpow(qpow(2,Rank),MOD-2)%MOD+MOD)%MOD);
return 0;
}

Solution2: DP

\(DP[i][j]\)表示选到第\(i\)个数,异或和为\(j\)的方案数

而每次转移只考虑第\(i\)个数取了奇数次还是偶数次,转移方程是\(DP[i][j] = DP[i-1][j XOR A_i] + DP[i-1][j]\)

最后答案就是\((\sum_{i=1}^{k}dp[tot][i])\cdot 2^{n-k}\)

最后的\(2^{n-k}\)是因为每个数出现不止一次,之前\(DP\)的时候每次只考虑了这个数选和不选,现在我们如果先固定选了几次,如果固定次数是奇数,那么我现在再去选它,相当于不选(因为偶数次选择的异或和为0)所以剩下\(n-k\)个重复出现的数可以任意使用

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const LL MOD = 1e9+7;
const int MAXN = 1e7+7;
char n[MAXN];
LL x1,a,b,c,d,e;
int k,base[13],len;
LL qpow(LL x, LL y){
LL ret = 1;
while(y){
if(y&1) ret = ret * x % MOD;
y >>= 1;
x = x * x % MOD;
}
return ret;
}
int f(LL x){ return (a*x*x*x*x+b*x*x*x+c*x*x+d*x+e-1) % k + 1; }
int dp[2][1<<13];
int main(){
scanf("%s %lld %lld %lld %lld %lld %lld %d",n,&x1,&a,&b,&c,&d,&e,&k);
int num = k;
len = strlen(n);
if(len<6){
int tp = 0;
for(int i = 0; i < len ; i++) tp = tp * 10 + n[i] - '0';
num = min(num,tp);
}
set<int> S;
for(int i = 1; i <= num; i++, x1 = f(x1)){
if(S.count(x1)) break;
S.insert(x1);
}
int tg = 0;
dp[tg][0] = 1;
for(int x : S){
tg ^= 1;
memset(dp[tg],0,sizeof(dp[tg]));
for(int i = 0; i < 4096; i++) dp[tg][i] = (dp[tg^1][i] + dp[tg^1][i^x]) % MOD;
}
LL sum = 0; for(int i = 1; i < 4096; i++) sum = (sum + dp[tg][i]) % MOD;
LL pw = 0;
for(int i = 0; i < len; i++) pw = (pw * 10 + n[i] - '0') % (MOD - 1);
LL pn = qpow(2,pw);
printf("%lld\n",(sum*pn%MOD*qpow(qpow(2,S.size()),MOD-2)%MOD+MOD)%MOD);
return 0;
}

L. Magical Girl Haze

分层图最短路,\(dist[i][j]\)表示到第\(i\)个位置,消耗变\(0\)用了\(j\)次的最小消耗

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
using LL = int_fast64_t;
const LL INF = 0x3f3f3f3f3f3f3f3f;
LL dist[MAXN][11];
int n,m,k;
vector<pair<int,int> > G[MAXN];
LL Dijkstra(){
memset(dist,0x3f,sizeof(dist));
priority_queue<pair<LL,pair<int,int>>,vector<pair<LL,pair<int,int>>>,greater<pair<LL,pair<int,int>>> > que;
dist[1][0] = 0;
que.push(make_pair(dist[1][0],make_pair(1,0)));
while(!que.empty()){
auto p = que.top();
que.pop();
if(dist[p.second.first][p.second.second]!=p.first) continue;
int u = p.second.first, deg = p.second.second;
for(auto e : G[u]){
int v = e.first, w = e.second;
if(dist[u][deg]+w<dist[v][deg]){
dist[v][deg] = dist[u][deg] + w;
que.push(make_pair(dist[v][deg],make_pair(v,deg)));
}
if(deg!=k and dist[u][deg]<dist[v][deg+1]){
dist[v][deg+1] = dist[u][deg];
que.push(make_pair(dist[v][deg+1],make_pair(v,deg+1)));
}
}
}
LL ret = INF;
for(int i = 0; i <= k; i++) ret = min(ret,dist[n][i]);
return ret;
}
void solve(){
scanf("%d %d %d",&n,&m,&k);
for(int i = 1; i <= n; i++) G[i].clear();
for(int i = 1; i <= m; i++){
int u, v, w;
scanf("%d %d %d",&u,&v,&w);
G[u].push_back(make_pair(v,w));
}
printf("%lld\n",Dijkstra());
}
int main(){
int T;
for(scanf("%d",&T); T; T--) solve();
return 0;
}

ACM-ICPC 2018 南京赛区网络预赛(12/12)的更多相关文章

  1. ACM-ICPC 2018 南京赛区网络预赛 J.sum

    A square-free integer is an integer which is indivisible by any square number except 11. For example ...

  2. 计蒜客 30999.Sum-筛无平方因数的数 (ACM-ICPC 2018 南京赛区网络预赛 J)

    J. Sum 26.87% 1000ms 512000K   A square-free integer is an integer which is indivisible by any squar ...

  3. 计蒜客 30990.An Olympian Math Problem-数学公式题 (ACM-ICPC 2018 南京赛区网络预赛 A)

    A. An Olympian Math Problem 54.28% 1000ms 65536K   Alice, a student of grade 66, is thinking about a ...

  4. ACM-ICPC 2018 南京赛区网络预赛

    轻轻松松也能拿到区域赛名额,CCPC真的好难 An Olympian Math Problem 问答 只看题面 54.76% 1000ms 65536K   Alice, a student of g ...

  5. ACM-ICPC 2018 南京赛区网络预赛 E题

    ACM-ICPC 2018 南京赛区网络预赛 E题 题目链接: https://nanti.jisuanke.com/t/30994 Dlsj is competing in a contest wi ...

  6. ACM-ICPC 2018 南京赛区网络预赛B

    题目链接:https://nanti.jisuanke.com/t/30991 Feeling hungry, a cute hamster decides to order some take-aw ...

  7. 计蒜客 30996.Lpl and Energy-saving Lamps-线段树(区间满足条件最靠左的值) (ACM-ICPC 2018 南京赛区网络预赛 G)

    G. Lpl and Energy-saving Lamps 42.07% 1000ms 65536K   During tea-drinking, princess, amongst other t ...

  8. ACM-ICPC 2018 南京赛区网络预赛 B. The writing on the wall

    题目链接:https://nanti.jisuanke.com/t/30991 2000ms 262144K   Feeling hungry, a cute hamster decides to o ...

  9. ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze

    262144K   There are NN cities in the country, and MM directional roads from uu to v(1\le u, v\le n)v ...

随机推荐

  1. MQ for linux安装与卸载【转】

    MQ for linux安装与卸载[转] 一.安装步骤:1. 用root帐号登录系统2. MQ安装程序需将代码安装到目录/opt/mqm下,将数据保存到目录/var/mqm下,需确保相关目录下有足够的 ...

  2. C++ 简单输出当前日期时间

    根据https://www.runoob.com/cplusplus/cpp-date-time.html编写. 首先介绍2个数据类型. 一个是time_t,与时间函数相关的变量,定义的变量记录着自 ...

  3. 【Java基础】Eclipse 和数组

    Eclipse 和数组 Eclipse 安装和使用 ... 数组的概述 数组(Array):是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理. 数组相 ...

  4. 【MyBatis】MyBatis 延迟加载策略

    MyBatis 延迟加载策略 文章源码 什么是延迟加载 延迟加载,就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据,也被成为懒加载. 好处:先从单表查询,需要时再从关联表去关联查询,大大提 ...

  5. 【Flutter】容器类组件之剪裁

    前言 Flutter中提供了一些剪裁函数,用于对组件进行剪裁. 剪裁Widget 作用 ClipOval 子组件为正方形时剪裁为内贴圆形,为矩形时,剪裁为内贴椭圆 ClipRRect 将子组件剪裁为圆 ...

  6. Jenkins上实现Python + Jenkins + Allure Report 接口自动化测试持续集成,最终测试报告用allure-report进行展示

    项目介绍 接口功能测试应用:http://www.weather.com.cn/data/cityinfo/<city_code>.html 测试功能:获取对应城市的天气预报 源码:Pyt ...

  7. k8s之ServiceAccount

    导读 上一篇说了k8s的RBAC授权模式,今天就来简单看一下其中涉及到的ServiceAccount. 简介 k8s创建两套独立的账号系统,原因如下: (1)User账号给用户用,Service Ac ...

  8. 【Problems】MySQL5.7 datetime 默认值设为‘0000-00-00 00:00:00'值出错

    记录 MySQL5.7 datetime 默认值设为'0000-00-00 00:00:00'值出错 我的MySQL版本 mysql --version 5.7.28 C:\Users\x1c> ...

  9. 【Oracle】Script to Collect DRM Information (drmdiag.sql) (文档 ID 1492990.1)

    脚本对应如下: The following (drmdiag.sql) is a script to collect information related to DRM (Dyanamic Reso ...

  10. SDUST数据结构 - chap4 串

    函数题: 6-1 查找子串: 裁判测试程序样例: #include <stdio.h> #define MAXS 30 char *search(char *s, char *t); vo ...