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. 使用vs code搭建Q#开发环境 (Mac)

    Q# 是微软几年前发布的一门用于模拟量子编程的语言. 3年前我在当时风靡的博客网站 ITEYE 上发布过如何在windows上搭建其开发环境:Q#开发环境搭建.时过境迁,不但iteye不知何处去,连Q ...

  2. LeetCode117 每个节点的右向指针 II

    给定一个二叉树 struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *next; } 填充它的每个 ...

  3. Openstack Ocata 公共服务端(三)

    Openstack Ocata 公共服务端 mysql 安装: yum install mariadb mariadb-server mysql 安装过程省略 rabbit-server 安装包: # ...

  4. AvaloniaUI体验

    公司的原有的PC端WPF产品有跨平台需求,无奈微软自己的xamarin对wpf的支持当前尚未达到能支撑产品的成熟度,于是经过搜索,发现了一个号称用WPF实现跨平台的第三方图形库AvaloniaUI. ...

  5. P1967 货车运输(倍增LCA,生成树)

    题目链接: https://www.luogu.org/problemnew/show/P1967 题目描述 A国有n座城市,编号从 1到n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制, ...

  6. 安装newman error:package exports for 'c:\nmp\node_modules\newman\node_module 解决办法

    一.场景描述: 通过npm安装newman时,一直失败. 尝试了很多安装命令: npm install -g newman npm install -g newman --registry=http: ...

  7. RCE - Pikachu

    概述: 远程系统命令执行 一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口 比如我们常见的路由器.防火墙.入侵检测等设备的web管理界面上 一般会给用户提供一个ping操 ...

  8. SwiftUI 中一些和响应式状态有关的属性包装器的用途

    SwiftUI 借鉴了 React 等 UI 框架的概念,通过 state 的变化,对 View 进行响应式的渲染.主要通过 @State, @StateObject, @ObservedObject ...

  9. 用SAP浏览网页

    在SAP里,通过两个类就可以做一个简单的,嵌入sap里的网页.这两个类就是 1. cl_gui_custom_container 这个类是自定义屏幕里用得,也就是画一个container,在这个容器中 ...

  10. Docker 如何动态给SpringBoot项目传参

    关于SpringBoot配置数据源 在项目开发中,我们往往需要配置多套不同的配置环境例如:本地开发.测试环境.部署环境.每一个环境的数据源配置可能都不同,因此需要写不同的数据源配置.如果用Docker ...