没代码的就是队友写的,只提供思路
E
签到题,首先可以算出两人胜利的场次数,答案就是较小的那个*2+1
K
找规律题,打个表可以发现输出 \(n\) 到 \(1\) 即可
A
签到题,枚举每种正方形的边对应的向量,则能构成该种正方形的顶点个数应当构成一个矩形,直接二维差分即可
G
赛事写的根号分治,然而可以根本不用
记下所有数出现的位置,已经出现次数 \(c_i\)
对于一次询问,直接枚举两个数中出现次数较小的数的所有位置,二分另一处即可
复杂度证明应该类似启发式(笔者没证),注意记忆化或特判相等情况就好了
B
这题跟 AGC023F 思路做法一模一样,感觉出题人就是根据这个出的
只要做过这个题的基本都能过(然而我们队没判相等条件,喜提 WA )
首先我们的攻击力是不变的,可以先算出每个怪兽需要打的次数 \(d_i\),设进入 \(u\) 点时我们的防御力为 \(T\) ,则我们经过这个点的扣血量为 \(d_i*(atk_i-Y)= d_i*atk_i-d_i*Y\),前者固定,最大化 \(d_i*Y\) 即可
问题转化为 ,经过点可以给 \(Y\) 加一或给答案增加 \(d_i*Y\) ,问答案最大值
类似那题,我们考虑贪心,每次将最优点向父亲合并,表示进行完父亲以及之前已经向父亲合并的所有节点后再走这个点
如何找最优解,考虑临项交换,设该已经相互合并的点的 \(t_i=1\) 的节点个数为 \(c_u\) , \(d_i\) 之和为 \(r_u\),考虑两个不同的集合 \(u,v\),两种情况分别为
1.\(Y\) 进 \(u\) 贡献的答案 \(+\) \(Y\) 进 \(v\) 贡献的答案 \(+\) \(c_u*r_v\) (先进 \(u\) 后进 \(v\))
2.\(Y\) 进 \(v\) 贡献的答案 \(+\) \(Y\) 进 \(u\) 贡献的答案 \(+\) \(c_v*r_u\) (先进 \(u\) 后进 \(v\))
因此只需比较不同的代价即可知按 \(\frac{c_u}{r_u}\) 排序即可
注意相等情况
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,X;
struct edge{int to,nex;
}e[640010];
int head[200100],cnt;
void add(int u,int v)
{e[++cnt]={v,head[u]};head[u]=cnt;
}
int fa[200100];
int dsu[200100];
void dfs(int now,int fath)
{fa[now]=fath;for(int i=head[now];i;i=e[i].nex){int v=e[i].to;if(v==fath) continue;dfs(v,now);}
}
long long ans;
struct node{int x,y,id;bool friend operator<(node a,node b){if(a.x*b.y==a.y*b.x){if(a.x==b.x){return a.id<b.id;}return a.x>b.x;}return a.x*b.y>a.y*b.x;}
}a[640010];
set<node>q;
int find(int x){return x==dsu[x]?x:dsu[x]=find(dsu[x]);}
signed main()
{// freopen("txt.in","r",stdin);// freopen("txt.out","w",stdout);cin>>n>>X;for(int i=1;i<n;i++){int u,v;cin>>u>>v;add(u,v);add(v,u);}dfs(1,0);for(int i=2;i<=n;i++){int t;cin>>t;if(t==1){a[i].x=1;a[i].y=0;}else {int atk,def,hp;cin>>atk>>def>>hp;a[i].x=0;a[i].y=(hp%(X-def)==0?hp/(X-def):hp/(X-def)+1)-1;ans-=a[i].y*atk;}a[i].id=i;q.insert(a[i]);}for(int i=1;i<=n;i++) dsu[i]=i;while(!q.empty()){node u=*q.begin();q.erase(q.begin());int fai=find(fa[u.id]);if(fai!=1) q.erase(q.find(a[fai]));ans+=a[fai].x*a[u.id].y;a[fai].x+=a[u.id].x;a[fai].y+=a[u.id].y;dsu[u.id]=fai;if(fai!=1) q.insert(a[fai]);}cout<<ans<<endl;return 0;
}
C
据说有巨多种贪心方法,这里讲一下我们队赛事的方法
见完全图最小生成树考虑 Boruvka 算法
只需靠考虑找出 $ (t_v+t_u)\mod k$ 的最小值即可,枚举 \(t_u\) 显然能成为最小值的 \(v\) 只能是 \(\leq 0\) 或 \(\leq k-t_u\) 的最小值取其一,直接用 set 即可
然而我们队非得写线段树二分(感觉赛事我们好傻)
贴个赛事代码(写的又臭又长,没啥参考性)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10,M=6e6+10,inf=2e9;
int t[N];
int sum[M],ls[M],rs[M];
int idm[M],idmt[M];
int vis[N],ccnt;
set<int>id[N];
int clr[N],clrtot;
struct dsu{int fa[N];inline void init(int n){for(int i=1;i<=n;i++) fa[i]=i;}inline int find(int x){return fa[x]==x?x:fa[x] =find(fa[x]);}
}dsu;
inline void upd(int &p,int l,int r,int pos,int v,int x){if(!p) p = ++ccnt, idm[p]=inf,idmt[p]=-1,ls[p]=rs[p]=sum[p]=0;if(l==r) {sum[p] += v;pos=lower_bound(clr+1,clr+1+clrtot,pos)-clr;if(v==1) id[pos].insert(x);else id[pos].erase(x);if(sum[p]>=1) idm[p]=l,idmt[p]=*id[pos].begin();else idm[p]=inf,idmt[p]=-1;return ;}int mid=(l+r)>>1;if(pos<=mid) upd(ls[p],l,mid,pos,v,x);else upd(rs[p],mid+1,r,pos,v,x);sum[p] = sum[ls[p]] + sum[rs[p]];if(ls[p]&&idm[ls[p]]!=inf) idm[p]=idm[ls[p]],idmt[p]=idmt[ls[p]];else if(rs[p]) idm[p]=idm[rs[p]],idmt[p]=idmt[rs[p]];else idm[p]=inf;if(idm[p]==inf) idmt[p]=-1;return ;
}
inline int findfirst(int p,int l,int r,int bound){if(!p) return -1;if(bound<=l) return idmt[p];if(l==r){if(sum[p]) return *id[lower_bound(clr+1,clr+1+clrtot,l)-clr].begin();else return -1;}int mid=(l+r)>>1;if(bound<=mid){int rl=findfirst(ls[p],l,mid,bound);if(rl!=-1) return rl;}return findfirst(rs[p],mid+1,r,bound);
}
vector<int>vc[N];
int rt;
struct node{int u,v,w;
}e[N];
int cnt;
bool cmp(node x,node y){return x.w<y.w;}
int main(){int T;cin>>T;while(T--){int n,k;cin>>n>>k;for(int i=1;i<=n;i++){cin>>t[i];t[i]%=k;vc[i].push_back(i);clr[++clrtot]=t[i];}sort(clr+1,clr+1+n);clrtot=unique(clr+1,clr+1+n)-clr-1;for(int i=1;i<=n;i++){upd(rt,0,k,t[i],1,i);}int tot=n;long long ans=0;dsu.init(n);while(1){for(int i=1;i<=n;i++) vis[i]=0; if(tot==1) break;cnt=0;dsu.init(n);for(int i=1;i<=tot;i++){for(int j=0;j<vc[i].size();j++){dsu.fa[vc[i][j]]=vc[i][0];upd(rt,0,k,t[vc[i][j]],-1,vc[i][j]);}for(int j=0;j<vc[i].size();j++){int now=t[vc[i][j]];int l=findfirst(rt,0,k,0),r=findfirst(rt,0,k,k-now);if(r==-1) {cnt++;e[cnt]={vc[i][j],l,(now+t[l])%k};}else if(l==-1) {cnt++;e[cnt]={vc[i][j],r,(now+t[r])%k};}else if((now+t[l])%k<=(now+t[r])%k){cnt++;e[cnt]={vc[i][j],l,(now+t[l])%k};}else {cnt++;e[cnt]={vc[i][j],r,(now+t[r])%k};}}for(int j=0;j<vc[i].size();j++){upd(rt,0,k,t[vc[i][j]],1,vc[i][j]);}}sort(e+1,e+1+cnt,cmp);for(int i=1;i<=cnt;i++){int u=dsu.find(e[i].u),v=dsu.find(e[i].v);if(u!=v){ans+=e[i].w;dsu.fa[v]=u;}}for(int i=1;i<=tot;i++){vc[i].clear();}tot=0;for(int i=1;i<=n;i++){if(dsu.fa[i]==i){tot++;vis[i]=tot;vc[tot].push_back(i);}}for(int i=1;i<=n;i++){if(dsu.fa[i]!=i){vc[vis[dsu.find(i)]].push_back(i);}}}cout<<ans<<endl;for(int i=1;i<=clrtot;i++) id[i].clear();clrtot=0;ccnt=rt=0;for(int i=1;i<=n;i++) vc[i].clear();}return 0;
}
D
典题,注意到通配符至多只有 \(10\) 个,考虑先删去通配符,将剩余的每个字符串都跑一边 kmp
枚举子串的左端点 \(l\) ,由于通配符的存在,我们可以尽可能贪心的匹配最后一个通配符之前的所有字符串,再找到最后一个字符串所有符合条件的位置。
注意不要写二分,用双指针即可
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define int long long
const int N=5e5+10;
ull hs[N],pw[N];
inline ull geths(int l,int r){return hs[r] - hs[l-1] * pw[r-l+1];}
char s[N],t[N];
const ull base = 13331;
signed main(){// ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);scanf("%s%s",s+1,t+1);int n = strlen(s+1),m= strlen(t+1);pw[0] = 1;for(int i=1;i<=m;++i){hs[i] = hs[i-1] * base + t[i];pw[i] = pw[i-1] * base;}string lst = "";bool lef = s[1] == '*';bool rig = s[n] == '*';vector<string> sub;for(int i=1;i<=n;++i){if(s[i] == '*'){if(lst != "") sub.emplace_back(lst);lst = "";}else{lst += s[i];}}if(!rig) sub.emplace_back(lst),lst = "";// if(sub.size() == 0) cout<<m*(m+1)/2<<endl,exit(0);vector<vector<int> > lpos(sub.size());vector<int> iter(sub.size());int cnt = 0,ans = 0; for(int it = 0;it<sub.size();++it){string str = sub[it];ull hs_s = 0;for(int j=0;j<str.length();++j) hs_s = hs_s * base + str[j];for(int j=1;j<=m;++j){if(j+str.length()-1>m) break;if(geths(j,j+str.length()-1) == hs_s) lpos[cnt].emplace_back(j);}// for(auto x:lpos[cnt]) cout<<x<<" "; cout<<endl;cnt += 1;}for(int l=1;l<=m;++l){int tar = l;bool flag = true;for(int j=0;j<cnt;++j){while(iter[j] < lpos[j].size() && lpos[j][iter[j]]<tar) iter[j] ++;if(iter[j]>=lpos[j].size()){flag = false;break;}tar = lpos[j][iter[j]] + sub[j].size();}if(!flag) break;// tar is the minimum Rif(lef == false && lpos[0][iter[0]] != l) continue;if(rig){if(cnt != 0) ans += m- tar + 2;else ans += m-l+1;}else{ans += lpos[cnt -1].size() - iter[cnt-1];}}printf("%lld\n",ans);return 0;
}
F
典中典博弈论,赛事就是不打表(恼)
首先考虑博弈论部分 ,参考这个题的打表 (CF1704F),你会发现这两个题的 \(SG\)计算过程一模一样,直接套过来就行了
对于初始局面的寻找,可以用 XOR hash 快速定位出所有的子游戏,然后就做完了
H
观察一个性质,每一门课最终只会被一个老师影响
因此我们可以钦定每门课被那个老师影响,即使不合法(比如选了另一个老师的值大于当前钦定的)也不影响答案,因为合法的显然更优
因此考虑 \(dp\) ,记 \(dp_{i,j,S}\) 表示考虑到前 \(i\) 个老师,用了 \(j\) 时间,已经钦定的课程集合为 \(S\) ,直接转移即可
注意转移时不要枚举子集,可以枚举每一门课,从该位为 0 的向 该位为 1 的所有状态转移,就可以做到 \(O(mTn2^n)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,k,T;
int len[55],t[55];
int l[55][20],to[55][20],clr[55][20],Si[55];
int v[20][10001];
int dp[2][51][16385];
int tmp[16385];
signed main()
{// freopen("txt.in","r",stdin);// freopen("txt.out","w",stdout);cin>>n>>m>>k>>T;int sum=0;for(int i=1;i<=n;i++){for(int j=0;j<=k;j++){cin>>v[i][j];}sum+=v[i][0];}for(int i=1;i<=m;i++){cin>>len[i]>>t[i];for(int j=1;j<=len[i];j++){cin>>l[i][j]>>to[i][j];Si[i]|=(1<<(l[i][j]-1));clr[i][l[i][j]]=v[l[i][j]][to[i][j]]-v[l[i][j]][0];}}memset(dp,-1,sizeof(dp));dp[0][0][0]=sum;int now_=0;for(int i=0;i<m;i++){for(int j=0;j<=T;j++){for(int S=0;S<(1<<n);S++){dp[now_^1][j][S]=max(dp[now_^1][j][S],dp[now_][j][S]);}}for(int j=0;j<=T-t[i+1];j++){for(int S=0;S<(1<<n);S++){tmp[S]=-1;}for(int rp=1;rp<=n;rp++){if(!(Si[i+1]&(1<<(rp-1)))) continue;for(int S=0;S<(1<<n);S++){if(!(S&(1<<(rp-1)))) {if(dp[now_][j][S]!=-1) tmp[S|(1<<(rp-1))]=max(tmp[S|(1<<(rp-1))],dp[now_][j][S]+clr[i+1][rp]);if(tmp[S]!=-1) tmp[S|(1<<rp-1)]=max(tmp[S|(1<<(rp-1))],tmp[S]+clr[i+1][rp]);}}}for(int S=0;S<(1<<n);S++){dp[now_^1][j+t[i+1]][S]=max(dp[now_^1][j+t[i+1]][S],tmp[S]);}}now_^=1;}for(int i=1;i<=T;i++){sum=0;for(int j=0;j<=i;j++){for(int S=0;S<=(1<<n)-1;S++) sum=max(sum,dp[now_][j][S]);}cout<<sum<<endl;}return 0;
}
其他题代补(也许还会补个 M)