【做题记录】HZOJ 多校-数论/多校-字符串/多校-图论Ⅲ
26. 数论 H. [arc137_d]Prefix XORs
对于普通的前缀和,有 \(S_i^{(k)}=\sum_{j=1}^{i}{i-j+k-1\choose k-1}a_j\),其中 \(S_i^{(k)}\) 表示 \(k\) 次前缀和后 \(a_i\) 的值(可以归纳证明)。
那么对于异或和,\(a_i\) 对第 \(k\) 次的答案有贡献的充要条件即为 \({n-i+k-1\choose k-1}\equiv1\pmod{2}\)。由卢卡斯定理可知它的充要条件又是 \((n-i)\mathrm{bitand}(k-1)=0\)。于是第 \(k\) 个答案即为:
高维前缀和即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=(1<<20)+5;
int n,m,a[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[n-i];
}
for(int i=0;i<=19;i++){
for(int S=0;S<1<<20;S++){
if(S>>i&1){
a[S]^=a[S^1<<i];
}
}
}
for(int i=0;i<m;i++){
cout<<a[((1<<20)-1)^i]<<' ';
}
return 0;
}
}
int main(){return asbt::main();}
28. 数论 L. [THUPC 2023 初赛] 背包
注意到 \(V\) 很大,考虑先用某个 \(\frac{c_i}{v_i}\) 最大的物品填很多个,然后再进行 DP。具体的 DP 考虑同余最短路,设 \(\frac{c_i}{v_i}\) 最大的物品的 \(c_i=w,v_i=m\),则对于任何一个 \(m\) 剩余系中的点,在最长路径中都不会经过很多次,因为不如换成若干个 \((m,w)\) 更优。
考虑对于询问 \(V\),一个背包方案 \((V',C')\ (V'\equiv V\pmod{m})\) 对应的答案是什么。其实就是将剩下的部分全都用 \((m,w)\) 填充,即为:
因此对于每个剩余系我们要求最大的 \(C'-\lfloor\frac{V'}{m}\rfloor\times w\)。于是有转移:
然而这是最长路,不能用 dijkstra,spfa 是绝对不会用的。怎么办呢?有一种很强的转圈技术可以做到时间复杂度 \(O(nm)\)。简单来说,因为最长路径上不可能经过同一个点 \(2\) 次,所以对于 \(v_i\) 这个物品在图中形成的 \(\gcd(v_i,m)\) 个环,绕着每个环转两圈转移一遍即可。因为不会重复经过一个点,因此所有 \(P'\le m^2=10^{10}<V\),故有正确性。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e5+5,inf=1e18;
int n,q,v[55],c[55],f[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>q;
int m=1,w=0;
for(int i=1;i<=n;i++){
cin>>v[i]>>c[i];
if(w*v[i]<m*c[i]){
m=v[i],w=c[i];
}
}
memset(f,-0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=__gcd(v[i],m)-1;~j;j--){
for(int u=j,cnt=0;cnt<=1;cnt+=u==j){
f[(u+v[i])%m]=max(f[(u+v[i])%m],f[u]+c[i]-(u+v[i])/m*w);
u=(u+v[i])%m;
}
}
}
while(q--){
int x;
cin>>x;
if(f[x%m]<=-inf){
cout<<-1<<'\n';
}else{
cout<<f[x%m]+x/m*w<<'\n';
}
}
return 0;
}
}
signed main(){return asbt::main();}
32. 字符串 A. 【模板】KMP
这又是啥
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=1e6+5;
int nxt[maxn];
string a,b;
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>a>>b;
int n=a.size(),m=b.size();
a=" "+a,b=" "+b;
for(int i=2,j=0;i<=m;i++){
while(j&&b[j+1]!=b[i]){
j=nxt[j];
}
if(b[j+1]==b[i]){
j++;
}
nxt[i]=j;
}
for(int i=1,j=0;i<=n;i++){
while(j&&b[j+1]!=a[i]){
j=nxt[j];
}
if(b[j+1]==a[i]){
j++;
}
if(j==m){
cout<<i-m+1<<'\n';
j=nxt[j];
}
}
for(int i=1;i<=m;i++){
cout<<nxt[i]<<' ';
}
return 0;
}
}
signed main(){return asbt::main();}
33. 图论 G. 【模板】欧拉路径
这都是啥
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
int n,m,in[maxn],out[maxn],c[maxn];
vector<int> e[maxn],ans;
il void dfs(int u){
for(int &i=c[u];i<e[u].size();){
dfs(e[u][i++]);
}
ans.pb(u);
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
out[u]++,in[v]++;
e[u].pb(v);
}
int s=0,t=0;
bool flag=0;
for(int i=1;i<=n;i++){
sort(e[i].begin(),e[i].end());
if(in[i]!=out[i]){
flag=1;
}
if(in[i]+1==out[i]){
if(s){
cout<<"No";
return 0;
}
s=i;
}else if(in[i]==out[i]+1){
if(t){
cout<<"No";
return 0;
}
t=i;
}
}
if((!s||!t)&&flag){
cout<<"No";
return 0;
}
if(!flag){
s=t=1;
}
// cout<<s<<' '<<t<<'\n';
// cout<<s;
dfs(s);
for(int i=m;~i;i--){
cout<<ans[i]<<' ';
}
return 0;
}
}
signed main(){return asbt::main();}
35. 数论 K. 牛场围栏
以最小的木料为同余最短路的模数,求出每个剩余系能凑出的最小的长度即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=3e3+5,inf=1e9;
int n,m,a[maxn],f[maxn];
bool vis[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,x;i<=n;i++){
cin>>x;
for(int j=0;j<=min(x-1,m);j++){
vis[x-j]=1;
}
}
int ma=0;
for(int i=1;i<=3e3;i++){
if(vis[i]){
ma=i;
break;
}
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=3e3;i++){
if(!vis[i]){
continue;
}
for(int j=__gcd(i,ma)-1;~j;j--){
for(int u=j,cnt=0;cnt<=1;cnt+=u==j){
f[(u+i)%ma]=min(f[(u+i)%ma],f[u]+i);
u=(u+i)%ma;
}
}
}
int ans=-1;
for(int i=0;i<ma;i++){
ans=max(ans,f[i]-ma);
}
cout<<(ans>=inf?-1:ans);
return 0;
}
}
signed main(){return asbt::main();}
36. 图论 D. [COCI2017-2018#5] Planinarenje
显然是二分图博弈虽然我咋不会,有如下结论:如果出发点一定在最大匹配上则先手必胜,否则后手必胜。于是先跑匈牙利,再在跑出的匹配上找出不一定在最大匹配上的点即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=5e3+5;
int n,m,vis[maxn],mch[maxn];
bool f[maxn];
vector<int> e[maxn];
il bool dfs1(int u,int tag){
for(int v:e[u]){
if(vis[v]!=tag){
vis[v]=tag;
if(!mch[v]||dfs1(mch[v],tag)){
mch[v]=u;
return 1;
}
}
}
return 0;
}
il void dfs2(int u){
f[u]=1;
for(int v:e[u]){
if(~vis[v]){
vis[v]=-1;
dfs2(mch[v]);
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
e[u].pb(v);
}
for(int i=1;i<=n;i++){
if(!dfs1(i,i)){
dfs2(i);
}
}
for(int i=1;i<=n;i++){
cout<<(f[i]?"Mirko":"Slavko")<<'\n';
}
return 0;
}
}
signed main(){return asbt::main();}
37. 字符串 E. Anthem of Berland
考虑 DP。设 \(f_i\) 表示 \(s_{1\sim i}\) 最多匹配多少个 \(t\)。考虑转移,如果 \(s_{i-m+1\sim i}\) 能直接与 \(t\) 匹配,显然可以转移。但并不完全,因为 \(t\) 可以重叠,因此需要跳失配数组。但此时又出大问题,我们不能保证枚举到的失配指针能在上一个位置匹配上。于是我们再设 \(g_i\) 表示强制 \(i\) 位置匹配的最大值即可。时间复杂度 \(O(nm)\)。
尝试带空格马蜂,这也太困难了吧
Code
#include <bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt {
const int maxn = 1e5 + 5;
int n, m, nxt[maxn], f[maxn], g[maxn];
string s, t;
il bool check(int p) {
for(int i = 1; i <= m; i++) {
if(s[p - i + 1] != t[m - i + 1] && s[p - i + 1] != '?') {
return 0;
}
}
return 1;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> s >> t;
n = s.size(), m = t.size(), s = " " + s, t = " " + t;
for(int i = 2, j = 0; i <= m; i++) {
while(j && t[j + 1] != t[i]) {
j = nxt[j];
}
if(t[j + 1] == t[i]) {
j++;
}
nxt[i] = j;
}
for(int i = m; i <= n; i++) {
f[i] = f[i - 1];
if(!check(i)) {
continue;
}
g[i] = f[i - m] + 1;
for(int j = nxt[m]; j; j = nxt[j]) {
g[i] = max(g[i], g[i - m + j] + 1);
}
f[i] = max(f[i], g[i]);
}
cout << f[n];
return 0;
}
}
signed main() {return asbt::main();}
39. 图论 H. Melody
对于每个声音连边 \(v_i\longleftrightarrow p_i\),跑个欧拉路径即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define pk pop_back
#define lwrb lower_bound
#define pii pair<int,int>
#define mp make_pair
#define fir first
#define sec second
using namespace std;
namespace asbt{
const int maxn=4e5+5;
int T,n,a[maxn],b[maxn],la[maxn],lb[maxn],deg[maxn],fa[maxn];
bool vis[maxn];
vector<pii> e[maxn];
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void dfs(int u){
while(e[u].size()){
int v=e[u].back().fir,t=e[u].back().sec;
e[u].pk();
if(e[v].size()&&!vis[t]){
vis[t]=1;
dfs(v);
cout<<t<<' ';
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
cin>>n;
int ta=0,tb=0;
for(int i=1;i<=n;i++){
cin>>a[i]>>b[i];
la[++ta]=a[i];
lb[++tb]=b[i];
vis[i]=0;
}
sort(la+1,la+ta+1);
sort(lb+1,lb+tb+1);
ta=unique(la+1,la+ta+1)-la-1;
tb=unique(lb+1,lb+tb+1)-lb-1;
for(int i=1;i<=ta+tb;i++){
deg[i]=0,fa[i]=i;
e[i].clear();
}
for(int i=1;i<=n;i++){
a[i]=lwrb(la+1,la+ta+1,a[i])-la;
b[i]=lwrb(lb+1,lb+tb+1,b[i])-lb+ta;
deg[a[i]]++,deg[b[i]]++;
fa[find(a[i])]=find(b[i]);
e[a[i]].pb(mp(b[i],i));
e[b[i]].pb(mp(a[i],i));
}
int s=0,t=0;
bool flag=0;
for(int i=1;i<=ta+tb;i++){
if(find(i)==i){
if(flag){
cout<<"No\n";
goto togo;
}
flag=1;
}
if(deg[i]&1){
if(!s){
s=i;
}else if(!t){
t=i;
}else{
cout<<"no\n";
goto togo;
}
}
}
if(!s&&!t){
s=t=1;
}else if(!s||!t){
cout<<"NO\n";
goto togo;
}
cout<<"YES\n";
dfs(s);
cout<<'\n';
togo:;
}
return 0;
}
}
signed main(){return asbt::main();}
40. 数论 Q. Koxia and Number Theory
首先不能有相同的数。
如果有四个数形如 \(2p_0,2q_0,2p_1+1,2q_1+1\),则必然无解,因为 \(x\) 取奇数/偶数都不行。
进一步推广,如果对于质数 \(P\),其每个剩余系中都有至少两个数,则必然无解。否则,设模 \(P\) 等于 \(k\) 的剩余系中只有一个/零个数,我们可以令 \(x\equiv-k\pmod{P}\),这样所有 \(a_i+x\) 必然没有 \(P\) 这个公因子。由 crt 知必然有解。于是我们枚举 \(n\) 以内的所有质数判断一遍即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
int T,n,cnt[105][105];
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>T;
while(T--){
for(int i:p){
for(int j=0;j<i;j++){
cnt[i][j]=0;
}
}
cin>>n;
set<ll> st;
bool flag=0;
for(int i=1;i<=n;i++){
ll x;
cin>>x;
if(flag){
continue;
}
// cout<<st.count(x)<<'\n';
if(st.count(x)){
cout<<"NO\n";
flag=1;
}
st.insert(x);
for(int j:p){
cnt[j][x%j]++;
}
}
if(flag){
goto togo1;
}
for(int i:p){
for(int j=0;j<i;j++){
if(cnt[i][j]<2){
goto togo2;
}
}
cout<<"NO\n";
goto togo1;
togo2:;
}
cout<<"YES\n";
togo1:;
}
return 0;
}
}
int main(){return asbt::main();}
/*
5
3
4 2 3
3
3 2 4
3
3 5 3
3
5 5 2
3
5 2 2
*/

浙公网安备 33010602011771号