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. 在mapper.xml映射文件中添加中文注释报错

    问题描述: 在写mapper.xml文件时,想给操作数据库语句添加一些中文注释,添加后运行报如下错误: 思考 可能是写了中文注释,编译器在解析xml文件时,未能成功转码,从而导致乱码.但是文件开头也采 ...

  2. Linux常用命令(df&dh)

    在Linux下查看磁盘空间使用情况,最常使用的就是du和df了.然而两者还是有很大区别的,有时候其输出结果甚至非常悬殊. du的工作原理 du命令会对待统计文件逐个调用fstat这个系统调用,获取文件 ...

  3. SparkSQL学习进度9-SQL实战案例

    Spark SQL  基本操作 将下列 JSON 格式数据复制到 Linux 系统中,并保存命名为 employee.json. { "id":1 , "name&quo ...

  4. BDC应用

    第一步:SHDB或者是SM35进入BDC录制事务.开始录制. 第二部:保存录制的记录. 第三步:在你自己的程序中定义一个内表如:ITAB TYPE TABLE OF BDCDATA. 再定义一个工作空 ...

  5. VBA调用数独求解器

    我开发了一个用于求解数独的dll文件,只需要双击一下注册表文件,就可以在VBA中调用这个功能了.具体步骤如下: 下载:https://share.weiyun.com/5dpcNqx 找到ExcelS ...

  6. 数据分析——Numpy/pandas

    NumPy NumPy是高性能科学计算和数据分析的基础包.部分功能如下: ndarray, 具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组. 用于对整组数据进行快速运算的标准数学函数(无需编 ...

  7. Mysql 中写操作时保驾护航的三兄弟!

    这期的文章主要是讲述写操作过程中涉及到的三个日志文件,看过前几期的话可能你或多或少已经有些了解了(或者从别的地方也了解过).比如整个写操作过程中用到的两阶段提交,又或者是操作过程中涉及到的日志文件,但 ...

  8. 一文说通Dotnet的委托

    简单的概念,也需要经常看看.   一.前言 先简单说说Delegate的由来.最早在C/C++中,有一个概念叫函数指针.其实就是一个内存指针,指向一个函数.调用函数时,只要调用函数指针就可以了,至于函 ...

  9. python(pymysql操作数据库)

    第一种方式 import pymysql # 打开数据库连接 db = pymysql.connect(host="192.168.88.11", user="root& ...

  10. .net core 和 WPF 开发升讯威在线客服与营销系统:使用 TCP协议 实现稳定的客服端

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...