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. centos 安装 部署 gitlab github

    https://www.cnblogs.com/wenwei-blog/p/5861450.html 我这里使用的是centos 7 64bit,我试过centos 6也是可以的! 1. 安装依赖软件 ...

  2. Flutter 应用入门:包管理

    pubspec.yaml name: flutter_combat description: A Flutter combat application. # The following defines ...

  3. 牛客剑指Offer-数字在升序数组中出现的次数

    题目 统计一个数字在升序数组中出现的次数. 示例1 输入 [1,2,3,3,3,3,4,5],3 返回值 4 题解 第一种最简单的方法是O(n)复杂度.遍历数组统计结果. public int Get ...

  4. MongoDB备份(mongodump)与恢复(mongorestore)工具实践

    mongodump和mongorestore实践 1.mongodump备份工具 mongodump能够在Mongodb运行时进行备份,它的工作原理是对运行的Mongodb做查询,然后将所有查到的文档 ...

  5. 【Java】Java关键字、含义

    Java关键字 来自 Java 核心技术卷I 基础知识(原书第10 版)/( 美)凯S 霍斯特曼(Cay S . Horstmann )著: 周立新等译一北京:机械工业出版社, 2016 . 8 Ja ...

  6. URL重定向 - Pikachu

    概述: 不安全的url跳转问题可能发生在一切执行了url地址跳转的地方.如果后端采用了前端传进来的(可能是用户传参,或者之前预埋在前端页面的url地址)参数作为了跳转的目的地,而又没有做判断的话就可能 ...

  7. i春秋新春战疫—web—简单的招聘系统

    打开靶机 打开后看到登录界面 利用万能密码,以admin身份登录 登录成功后看到如下界面 在Blank Page界面内发现注入点,抓包 保存在sqlmap目录下test.txt文件夹,使用sqlmap ...

  8. random模块常用函数

    random模块常用函数: from random import * # Random float: 0.0 <= x < 1.0 random() # Random float: 2.5 ...

  9. odoo之技巧合集一

    罗列一些odoo开发中的简单但有效的方法: 1.重写odoo登录代码 参考链接:odoo10-重写登录方法 from odoo import models, fields, api, SUPERUSE ...

  10. 03. struts2中Action配置的各项默认值

    Action中的各项默认值 Action各项配置 <action name="helloworld" class="com.liuyong666.action.He ...