文章目录
- [F. Colorful Balloons(签到)](#F. Colorful Balloons(签到))
- [E. Matrix Distances(思维+小结论)](#E. Matrix Distances(思维+小结论))
- [J. Takeout Delivering(最短路)](#J. Takeout Delivering(最短路))
- [G. Streak Manipulation(二分+dp)](#G. Streak Manipulation(二分+dp))
- [C. Cyclic Substrings(回文自动机)](#C. Cyclic Substrings(回文自动机))
F. Colorful Balloons(签到)

cpp
int n;cin>>n;
for(int i=1;i<=n;i++) cin>>s[i];
map<string,int> mp;
for(int i=1;i<=n;i++)
{
mp[s[i]]++;
if(mp[s[i]]*2 > n)
{
cout<<s[i];
return;
}
}
cout<<"uh-oh";
E. Matrix Distances(思维+小结论)

经典的x和y可以分开算
cpp
int n,m;cin>>n>>m;
vector<int> nums;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>c[i][j];
nums.push_back(c[i][j]);
}
sort(nums.begin(),nums.end());
nums.erase(unique(nums.begin(),nums.end()),nums.end());
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
c[i][j]=lower_bound(nums.begin(),nums.end(),c[i][j])-nums.begin();
}
}
int ans=0;
vector<int> num(nums.size()+1),sum(nums.size()+1);
for(int j=1;j<=m;j++) //
for(int i=1;i<=n;i++)
{
ans+=num[c[i][j]]*j - sum[c[i][j]];
sum[c[i][j]]+=j;
num[c[i][j]]++;
}
for(int i=0;i<nums.size()+1;i++)num[i]=sum[i]=0;
for(int i=1;i<=n;i++) //
for(int j=1;j<=m;j++)
{
ans+=num[c[i][j]]*i - sum[c[i][j]];
sum[c[i][j]]+=i;
num[c[i][j]]++;
}
cout<<ans*2;
J. Takeout Delivering(最短路)

题意:
从 1 1 1走到 n n n,路径大小为最大的两条边的边权和
思路:
分别从 1 , n 1,n 1,n开始记录到每个点的最大值
枚举最大边,那么答案就是 min ( w + max ( d i s 1 , u , d i s v , n ) ) ( max ( d i s 1 , u , d i s v , n ) ≤ w ) \min (w+\max(dis_{1,u},dis_{v,n}))(\max(dis_{1,u},dis_{v,n}) \leq w) min(w+max(dis1,u,disv,n))(max(dis1,u,disv,n)≤w)
cpp
void solve(){
int n,m;
cin>>n>>m;
vector<PII> adj[n+1];
int ans=INF;
vector<array<int,3>> e;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
if((u==1&&v==n)||(u==n&&v==1))ans=min(ans,w);
adj[u].push_back({v,w});
adj[v].push_back({u,w});
e.push_back({u,v,w});
}
vector<int> vis(n+1);
auto dijk=[&](vector<int> &dis,int st){
priority_queue<PII,vector<PII>,greater<PII>> pq;
dis[st]=0;
vis.assign(n+1,0);
pq.push({0,st});
while(!pq.empty()){
auto [d,u]=pq.top();
pq.pop();
if(vis[u])continue;
vis[u]=1;
for(auto [v,w]:adj[u]){
int x=(dis[u]==0?w:max(dis[u],w));
if(dis[v]>x){
dis[v]=x;
pq.push({dis[v],v});
}
}
}
};
vector<int> dis1(n+1,INF),dis2(n+1,INF);
dijk(dis1,1);
dijk(dis2,n);
for(auto [u,v,w]:e){
if(dis1[u]<=w&&dis2[v]<=w)ans=min(ans,w+max(dis1[u],dis2[v]));
if(dis1[v]<=w&&dis2[u]<=w)ans=min(ans,w+max(dis1[v],dis2[u]));
}
cout<<ans<<"\n";
}
G. Streak Manipulation(二分+dp)

给一个01字符串,问最大长度 l l l,这样的连续 1 1 1的不相连接线段至少有 k k k个,最多可以修改 m m m次 0 0 0变 1 1 1
很明显可以二分答案,并且 k k k很小可以直接dp
d p i , j dp_{i,j} dpi,j表示考虑完前 i i i个字母,构成了 j j j段长度为 l l l的线段,判断条件就是 d p n , k ≤ m dp_{n,k} \leq m dpn,k≤m
转移的时候我们枚举每个线段的合法右端点,然后获取大于等于 l l l的线段的左端点(这一部分最易出错,考虑各种边界即可,比如左右端点在原有的线段内,或者如果当前端点置1导致两个线段合并在一起不符和假设的左端点或右端点时,需要更新以下)
复杂度 O ( n log 2 n ) O(n \log^2n) O(nlog2n),实现好一点可以 O ( n log n ) O(n \log n) O(nlogn)
cpp
int n,m,k;
vector<PII> seg;
int sum[N];
bool check(int x){
vector dp(n+1,vector<int>(k+1,INF));
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++)dp[i][j]=dp[i-1][j];
auto it=upper_bound(seg.begin(),seg.end(),PII{i,INF});
if(it!=seg.end()&&it->F-1==i)continue;
if(it!=seg.begin()){
it--;
if(it->F<=i&&i<=it->S&&i!=it->S)continue;
}
int l=i-x+1;
if(l<1)continue;
it=upper_bound(seg.begin(),seg.end(),PII{l,INF});
if(it!=seg.begin()){
it--;
if(l<=it->S+1)l=it->F;
}
int pos=l==1?0:l-2;
for(int j=1;j<=k;j++){
if(dp[pos][j-1]==INF)continue;
dp[i][j]=min(dp[i][j],dp[pos][j-1]+sum[i]-sum[l-1]);
}
}
return dp[n][k]<=m;
}
void solve(){
cin>>n>>m>>k;
string s;
cin>>s;
s=" "+s;
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(s[i]=='0');
for(int i=1,j;i<=n;i++){
if(s[i]=='0')continue;
j=i;
while(j+1<=n&&s[j+1]==s[j])j++;
seg.push_back({i,j});
i=j;
}
int l=0,r=n;
while(l<r){
int mid=l+r+1>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<(l==0?-1:l);
}
C. Cyclic Substrings(回文自动机)

设 t t t为循环本质不同回文子串, f ( t ) f(t) f(t)表示出现次数, g ( t ) g(t) g(t)表示长度,求 ∑ f ( t ) 2 g ( t ) \sum f(t)^2g(t) ∑f(t)2g(t)
回文自动机板子题,令新串为 S = S S S=SS S=SS,那么就可以求出来 S S S的回文自动机,当前的回文串是有效的当下标 i > n i \gt n i>n
然后在fail树上做一遍累加,最后统计答案
cpp
struct PAM{
static constexpr int ALPHABET_SIZE=10;
struct Node{
int len,link,cnt;// cnt表示以当前字符结尾的不同子串个数(视实际情况而定)
array<int,ALPHABET_SIZE> next;
};
vector<Node> t;
int suff;
string s;
PAM(){
init();
}
void init(){
t.assign(2,Node());
t[0].len=-1;
suff=1;
s.clear();
}
int newNode(){
t.emplace_back();
return t.size()-1;
}
bool add(int c){
int pos=s.size();
s+=c;
c=c-'0';
int cur=suff,curlen=0;
while(true){
curlen=t[cur].len;
if(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos])break;
cur=t[cur].link;
}
if(t[cur].next[c]){
suff=t[cur].next[c];
return false;
}
suff=newNode();
t[suff].len=t[cur].len+2;
t[cur].next[c]=suff;
if(t[suff].len==1){
t[suff].link=1;
return true;
}
while(true){
cur=t[cur].link;
curlen=t[cur].len;
if(pos-1-curlen>=0&&s[pos-1-curlen]==s[pos]){
t[suff].link=t[cur].next[c];
break;
}
}
return true;
}
int next(int p,int c){
return t[p].next[c-'0'];
}
int link(int p){
return t[p].link;
}
int len(int p){
return t[p].len;
}
int cnt(int p){
return t[p].cnt;
}
int size(){
return t.size();
}
} pam;
void solve(){
int n;
cin>>n;
string s;
cin>>s;
s+=s;
auto &t=pam.t;
for(int i=0;i<s.size();i++){
pam.add(s[i]);
if(i>n-1)t[pam.suff].cnt++;
}
for(int i=t.size()-1;i>=2;i--){
t[t[i].link].cnt+=t[i].cnt;
}
LL ans=0;
for(int i=2;i<t.size();i++){
if(t[i].len<=n)ans+=1ll*t[i].cnt*t[i].cnt%mod*t[i].len%mod,ans%=mod;
}
cout<<ans<<"\n";
}