A Permutation
给定质数 p p p.求一个 p − 1 p-1 p−1的排列 a a a.
满足 ∀ i ∈ [ 2 , p − 1 ] , a [ i ] = a [ i − 1 ] ∗ 2 % p o r a [ i − 1 ] ∗ 3 % p \forall i\in [2,p-1],a[i]=a[i-1]*2\%p ~or ~ a[i-1]*3\% p ∀i∈[2,p−1],a[i]=a[i−1]∗2%p or a[i−1]∗3%p.
每个数向两倍连边,那么就一定形成若干个大小相同的环.
一个环都*3即连向下一个环,所以做法就是
∗
2
*2
∗2不行就
∗
3
*3
∗3.
int T,n,p,a[N];
bool v[N];
int main() {
qr(T); while(T--) {
qr(p); n=p-1;
for(int i=1;i<=n;i++) v[i]=0;
v[a[1]=1]=1;
bool flag=1;
for(int i=2;i<=n;i++) {
int x=a[i-1];
if(!v[x*2%p]) v[a[i]=x*2%p]=1;
else if(!v[x*3%p]) v[a[i]=x*3%p]=1;
else {flag=0;break;}
}
if(!flag) puts("-1");
else {
for(int i=1;i<=n;i++) pr1(a[i]);
puts("");
}
}
return 0;
}
B KMP Algorithm
参考blog:https://blog.csdn.net/weixin_43965698/article/details/107945812
字符串匹配不难想到卷积形式.
而这样做是
O
(
26
n
log
n
)
O(26n\log n)
O(26nlogn)的.
我们考虑利用性质.
设
f
[
i
]
=
(
s
[
i
]
=
=
c
)
,
g
[
i
]
=
(
t
[
i
]
=
=
c
)
f[i]=(s[i]==c),g[i]=(t[i]==c)
f[i]=(s[i]==c),g[i]=(t[i]==c).
设
D
F
[
i
]
=
f
[
i
]
−
f
[
i
+
d
]
,
D
G
[
i
]
=
g
[
i
]
−
g
[
i
+
d
]
DF[i]=f[i]-f[i+d],DG[i]=g[i]-g[i+d]
DF[i]=f[i]−f[i+d],DG[i]=g[i]−g[i+d].
可以
D
F
[
i
]
,
D
G
[
i
]
DF[i],DG[i]
DF[i],DG[i]只有
O
(
d
)
O(d)
O(d)个非0 位置,故暴力卷积即可.
(
D
F
∗
D
G
)
[
i
]
=
∑
x
+
y
=
i
D
F
[
x
]
⋅
D
G
[
y
]
=
∑
x
+
y
=
i
(
f
[
x
]
−
f
[
x
+
d
]
)
∗
(
g
[
y
]
−
g
[
y
+
d
]
)
=
∑
x
+
y
=
i
f
[
x
]
g
[
y
]
−
2
f
[
x
+
d
]
g
[
y
]
+
f
[
x
+
d
]
g
[
y
+
d
]
→
(
f
∗
g
)
[
i
]
=
(
D
F
∗
D
G
)
[
i
]
+
2
(
f
∗
g
)
[
i
+
d
]
−
(
f
∗
g
)
[
i
+
2
d
]
(DF*DG)[i]=\sum_{x+y=i} DF[x]\cdot DG[y]=\sum_{x+y=i}(f[x]-f[x+d])*(g[y]-g[y+d])=\sum_{x+y=i} f[x]g[y]-2f[x+d]g[y]+f[x+d]g[y+d]\rightarrow (f*g)[i]=(DF*DG)[i]+2(f*g)[i+d]-(f*g)[i+2d]
(DF∗DG)[i]=∑x+y=iDF[x]⋅DG[y]=∑x+y=i(f[x]−f[x+d])∗(g[y]−g[y+d])=∑x+y=if[x]g[y]−2f[x+d]g[y]+f[x+d]g[y+d]→(f∗g)[i]=(DF∗DG)[i]+2(f∗g)[i+d]−(f∗g)[i+2d].
int d,n,m,cnt[N],same[N];
char s[N],t[N];
vector<pii> f[28],g[28];
int main() {
scanf("%d %s %s",&d,s+1,t+1);
n=strlen(s+1); m=strlen(t+1); reverse(t+1,t+m+1);
for(int i=1;i<=n;i++) s[i] -= 'a'-1;
for(int i=1;i<=m;i++) t[i] -= 'a'-1;
for(int i=1;i<=d;i++) f[s[i]].pb(mk(i-d,-1)),g[t[i]].pb(mk(i-d,-1));
for(int i=1;i<=n;i++) if(s[i] != s[i+d]) {
f[s[i]].pb(mk(i,1));
if(i+d<=n) f[s[i+d]].pb(mk(i,-1));
}
for(int i=1;i<=m;i++) if(t[i] != t[i+d]) {
g[t[i]].pb(mk(i,1));
if(i+d<=m) g[t[i+d]].pb(mk(i,-1));
}
for(int c=1;c<=26;c++)
for(auto i:f[c])
for(auto j:g[c])
if(i.fi+j.fi>m) cnt[i.fi+j.fi-m] += i.se*j.se;
for(int i=n;i;i--) same[i]=cnt[i]+2*same[i+d]-same[i+2*d];
for(int i=1;i<=n-m+1;i++) pr2(m-same[i]);
return 0;
}
C Decrement on the Tree
给定一棵树,边带权.
一次操作为把一个路径上的所有边的权-1.
问最少操作数使得所有边的边权为0.
同时要能支持边权修改.
n , q ≤ 1 e 5 n,q\le 1e5 n,q≤1e5.
路径数等价于数端点数/2.
我们从每个点考虑.
设一个点
x
x
x连出的最大边为
m
x
,
s
[
x
]
mx,s[x]
mx,s[x]表示出边权值总和.,若
2
m
x
>
s
[
x
]
2mx> s[x]
2mx>s[x]
那么
2
m
x
−
s
[
x
]
2mx-s[x]
2mx−s[x]条边的端点为
x
x
x.
其他情况边权一定能够两两配对,剩余
s
[
x
]
%
2
s[x]\%2
s[x]%2条边的端点为
x
x
x.
用
m
u
l
t
i
s
e
t
multiset
multiset维护出边最大值即可.
总复杂度为
O
(
(
n
+
q
)
log
n
)
O((n+q)\log n)
O((n+q)logn).
int n,m;
ll s[N],ans;
multiset<int,greater<int> > w[N];
struct E{int x,y,z;} e[N];
int f(int x) {
int mx=*w[x].begin();
if(mx*2>s[x]) return mx*2-s[x];
return s[x]&1;
}
void add(int x,int y,int z) {
s[x] += z; s[y] += z;
w[x].insert(z);
w[y].insert(z);
ans += f(x)+f(y);
}
void del(int x,int y,int z) {
ans -= f(x)+f(y);
w[x].erase(w[x].find(z));
w[y].erase(w[y].find(z));
s[x] -= z; s[y] -= z;
}
int main() {
qr(n); qr(m);
for(int i=1,x,y,z;i<n;i++) {
qr(x); qr(y); qr(z);
add(x,y,z);
e[i]=(E){x,y,z};
}
ans=0;
for(int i=1;i<=n;i++) ans += f(i);
pr2(ans/2);
while(m--) {
int op,x,y,z; qr(op); qr(z);
x=e[op].x; y=e[op].y;
del(x,y,e[op].z);
add(x,y,e[op].z=z);
pr2(ans/2);
}
return 0;
}
D Hearthstone Battlegrounds
炉石小游戏.
有4种特效角色(A类):
- 剧毒+圣盾+亡语
- 剧毒+圣盾
- 剧毒+亡语
- 剧毒.
剧毒角色的属性为 1 / 1 0 9 1/10^9 1/109. x / y x/y x/y表示攻击为 x x x,生命为 y y y.
亡语的效果为召唤一只 1 / 1 1/1 1/1.(B类)
现在已知双方的角色数量,问先手是否用获胜概率.
我们要判定先手是否能赢.
实际上就是先手采取最优策略,后手采取最坏策略.
对于小兵
(
1
/
1
)
(1/1)
(1/1),先手要用肯定用来破盾.
而对于后手,肯定和A撞最傻.
我们的策略是:
- 尽量用亡语.
- 尽量先撞无盾的.
int a,b,c,d;
int e,f,g,h;
int s1,s2;
/*
1. 剧毒+圣盾+亡语
2. 剧毒+圣盾
3. 剧毒+亡语
4. 剧毒.
*/
bool fight() {
//破盾
if(s1 && e) {s1--; e--; g++; return 1;}
if(s1 && f) {s1--; f--; h++; return 1;}
//送命
if(s2 && (a+b+c+d)>0) {s2=0; return 1;}
//动用召唤去撞无盾
if(c && g) {c--; s1++; g--; s2++; return 1;}
if(a && g) {a--; c++; g--; s2++; return 1;}
if(c && h) {c--; s1++; h--; return 1;}
if(a && h) {a--; c++; h--; return 1;}
//对方有盾.先用召唤的去换,实在不行再用无召唤的搏.
if(c && e) {c--; s1++; e--; g++; return 1;}
if(c && f) {c--; s1++; f--; h++; return 1;}
if(a && e) {a--; c++; e--; g++; return 1;}
if(a && f) {a--; c++; f--; h++; return 1;}
if(b && e) {b--; d++; e--; g++; return 1;}
if(b && f) {b--; d++; f--; h++; return 1;}
if(d && e) {d--; e--; g++; return 1;}
if(d && f) {d--; f--; h++; return 1;}
//其他撞无盾
if(b && g) {b--; d++; g--; s2++; return 1;}
if(d && g) {d--; g--; s2++; return 1;}
if(b && h) {b--; d++; h--; return 1;}
if(d && h) {d--; h--; return 1;}
return 0;
}
int main() {
int T; qr(T); while(T--) {
qr(a); qr(b); qr(c); qr(d);
qr(e); qr(f); qr(g); qr(h);
s1=s2=0;
while(fight());
if(a+b+c+d>0) puts("Yes");
else if(e+f+g+h>0) puts("No");
else if(s1<=s2) puts("No");
else puts("Yes");
}
}
E Game
推箱子:给你一个 n n n列的仓库.每列堆放着一些 1 ∗ 1 ∗ 1 1*1*1 1∗1∗1的箱子.
你可以把箱子右移,使得最高的箱子最低.
前缀最大平均值
int n,T;
ll sum,x,ans;
int main() {
qr(T); while(T--) {
qr(n); ans=sum=0;
for(int i=1;i<=n;i++) {
qr(x); sum += x;
ans=max(ans,(sum+i-1)/i);
}
pr2(ans);
}
}
I Tournament
有 n n n支队伍,要求两两比赛.(总共 n ∗ ( n − 1 ) / 2 n*(n-1)/2 n∗(n−1)/2场)
一支队伍在首次比赛时入场,最后一次比赛后离开.
输出比赛方案使得所有队伍的在场时间和最小.
参考blog :https://blog.csdn.net/qq_45458915/article/details/107924613
看到样例,容易想到升序莽过去…
然而,这种方法劣在最小化1号队伍,其他队伍都要等很久.
我们可以考虑在升序的情况下优化.
如果我们前移
(
i
,
j
)
(
i
<
j
)
(i,j)(i<j)
(i,j)(i<j) (运到
(
1
,
j
+
1
)
(1,j+1)
(1,j+1)之前) 则变化为
i
−
1
−
(
n
−
j
)
i-1-(n-j)
i−1−(n−j).
即前面的
i
i
i的结束时间+1,
n
−
j
n-j
n−j个的开始时间-1.
由此我们可以先把所有满足
i
−
1
−
(
n
−
j
)
<
0
i-1-(n-j)<0
i−1−(n−j)<0的
(
i
,
j
)
(i,j)
(i,j)前移.
这一部分的话,有上述前移的位置可以知道要先按
j
j
j升序,再按
i
i
i升序.
剩下部分我们升序输出即可.(既然前移已经不能优化,那么我们肯定要让先加入的尽量快结束.)
qr(T); while(T--) {
qr(n);
for(int j=2;j<=n;j++)
for(int i=1;i<min(j,n-j+1);i++)
pr1(i),pr2(j);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(i>=min(j,n-j+1))
pr1(i),pr2(j);
}
J Identical Trees
给定两颗有根树,你需要修改树上标号使得两棵树一样.
求最小的编号修改次数. n ≤ 500 n\le 500 n≤500.
树形DP.
两两子树进行匹配.
然后用二分图最大匹配合并答案.
复杂度上界为 O ( n 4 ) O(n^4) O(n4),但是并不好卡,实际复杂度应该是 O ( n 3 ) O(n^3) O(n3)级别的.
const int N=510;
int n;
struct Graph {
int rt;
struct edge{int y,next; } a[N]; int len,last[N],deg[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len; }
ull h[N];
void dfs(int x) {
vector<ull> t;
for(int k=last[x],y;k;k=a[k].next) dfs(y=a[k].y),t.pb(h[y]);
sort(all(t)); h[x]=1; for(auto i:t) h[x]=h[x]*2333+i;
}
void init() {
for(int i=1,f;i<=n;i++) {
qr(f);
if(!f) rt=i;
else ins(f,i),deg[f]++;
}
dfs(rt);
}
} u,v;
namespace Flow {
const int M=N*N;
struct edge{int y,next,d,c;} a[M]; int len=1,last[N*2];
int q[N*2],d[N*2],pre[N*2],st,ed,T;
void ins(int x,int y,int c,int d) {a[++len]=(edge){y,last[x],d,c}; last[x]=len; }
void add(int x,int y,int c,int d) {ins(x,y,c,d); ins(y,x,0,-d); }
void clear(int x) {
T=x;
int *p=last+1; while(*p) *p++=0;
len=1; st=1; ed=2;
}
int EK() {
if(!T) return 0;
int ans=0,cnt=T;
while(1) {
int l=1,r=2; q[l]=st;
memset(d+1,63,sizeof(int)*(2*T+2)); d[st]=pre[ed]=0;//每次找最小边增广
while(l != r) {
int x=q[l++]; if(l==N*2) l=1;
for(int k=last[x],y;k;k=a[k].next)
if(d[y=a[k].y]>d[x]+a[k].d&&a[k].c) {
d[y]=d[x]+a[k].d;
pre[y]=k^1;
q[r++]=y;
if(r==N*2) r=1;
}
}
ans += d[ed];
if(!--cnt) return ans;//流必须流满
if(!pre[ed]) return M;//否则返回INF
int x=ed;
while(x^st) {
int k=pre[x];
a[k].c++;
a[k^1].c--;
x=a[k].y;
}
}
}
}
int f[N][N];
int solve(int x,int y) {
if(u.h[x] ^ v.h[y]) return N;//hash预判.
for(int A=u.last[x];A;A=u.a[A].next)
for(int B=v.last[y];B;B=v.a[B].next)
f[u.a[A].y][v.a[B].y]=solve(u.a[A].y,v.a[B].y);
int tot=u.deg[x],dx=2,dy;
if(tot ^ v.deg[y]) return N; //双重保险.(保证判定树同构的正确性)
Flow::clear(tot);
for(int A=u.last[x];A;A=u.a[A].next) {
++dx; Flow::add(1,dx,1,0); dy=tot+2;
for(int B=v.last[y];B;B=v.a[B].next) {
dy++; int val=f[u.a[A].y][v.a[B].y];
if(val<=n)
Flow::add(dx,dy,1,val);
if(dx == 3)Flow::add(dy,2,1,0);
}
}
return Flow::EK()+(x!=y);
}
int main() {
qr(n);
u.init(); v.init();
pr2(solve(u.rt,v.rt));
}