2025 GPCPC/JSCPC补题
在这里补一些没做出来的题,还是看不懂的就投降了。
J. Puzzle Competition
题意:有向图,每个点有激活的能量要求(其中必有0要求的点),激活后的点向所有边发出1点能量,经过边权的时间后抵达。然后还有一个自动启动装置,到达他的启动时间后,他会把他相连的所有点启动。
当时想到了拓扑排序,想到了自动启动装置作为一个点(但是发送的能量应是INF),但是没想到基于边的拓扑排序。好歹补的时候想到了。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl '\n'
#define fff(x,y,z) for(int x=(y);x<=(z);x++)
#define rrr(x,y,z) for(int x=(y);x>=(z);x--)
using pii = pair<int,int>;
using ari = array<int,3>;
const int MAX=1e5+2;
// const int MOD;
int N,M,K;
vector<pii> e[MAX];
struct Edge{
int ti,val,idx;
//传到时间 边值大小 目标点
bool operator<(const Edge&other)const{
if(ti==other.ti)return val>other.val;
return ti<other.ti;
}
bool operator>(const Edge&other)const{
if(ti==other.ti)return val<other.val;
return ti>other.ti;
}
};
priority_queue<Edge,vector<Edge>,greater<Edge>> pq;
void solve(){
cin>>N>>M>>K;
vector<int> a(N+1),ans(N+1,-1);
// vector<bool> vis(N+1);
fff(i,1,N)cin>>a[i];
fff(i,1,K){
int t,sc;cin>>t>>sc;
fff(j,1,sc){
int v;cin>>v;
pq.push({t,LONG_MAX,v});
}
}
fff(i,1,M){
int u,v,w;cin>>u>>v>>w;
e[u].emplace_back(make_pair(v,w));
}
fff(i,1,N){
if(a[i]==0){
// vis[i]=1;
ans[i]=0;
for(auto [v,w]:e[i]){
pq.push({w,1,v});
}
}
}
while(pq.size()){
auto [ti,val,cur]=pq.top();pq.pop();
if(a[cur]<=0)continue;
a[cur]-=val;
if(a[cur]<=0){
ans[cur]=ti;
for(auto [v,w]:e[cur]){
pq.push({ti+w,1,v});
}
}
}
fff(i,1,N)cout<<ans[i]<<' ';
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// int times;cin>>times;
// while(times--)
solve();
return 0;
}
H. Loose Subsequences
题意:本质不同子序列,但是多了个选出的字符距离大于K的限制。
考虑K==0时。此时就是本质不同子序列。对于第一次出现的字符,dp[i]=i+dp[1]+...+dp[i-1];否则对于last[str[i]]之前的,累加会与last[str[i]]重合,就不对了,只能累加last[str[i]]到i-1之间的。
考虑K>0时。此时多了K的限制。对于第一次出现的字符,dp[i]=i+dp[1]+...+dp[i-K-1];对于last[str[i]]-K之前的,累加会与last[str[i]]算过的重合,只能累加last[str[i]]-K到i-1之间的。
前缀和优化即可。同时要小心加不了的情况,要注意比较端点大小、以及端点小于0的情况。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl '\n'
#define fff(x,y,z) for(int x=(y);x<=(z);x++)
#define rrr(x,y,z) for(int x=(y);x>=(z);x--)
using pii = pair<int,int>;
using ari = array<int,3>;
// const int MAX;
const int MOD=998244353;
int N,M;
int las[27];
void solve(){
string str;
cin>>N>>M>>str;
int len=str.length();
str='*'+str;
vector<int> dp(N+1),pre(dp);
fill(las+1,las+1+26,0ll);
int ans=0;
for(int i=1;i<=len;i++){
int cs=str[i]-'a'+1;
if(las[cs]){
int lb=max(0ll,las[cs]-M-1);
int rb=max(0ll,i-M-1);
// if(i-M-1>las[cs]-1){
dp[i]=(pre[rb]-pre[lb]+MOD)%MOD;
// }
}else{
dp[i]=1+pre[max(i-M-1,0ll)];
}
dp[i]%=MOD;
pre[i]=pre[i-1]+dp[i];
pre[i]%=MOD;
las[cs]=i;
ans+=dp[i];
ans%=MOD;
// cout<<dp[i]<<' ';
}
// cout<<endl;
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int times;cin>>times;
while(times--)
solve();
return 0;
}
A. Matrix Game
题意:给你个01组成的矩阵,任意反转行与列(01互换),要你求所有a{i,j}==1的A*i+B*j的和最大时的结果。
m很小,考虑枚举2^m种列的反转情况,然后考察行反转情况
定义cnt为一行的1的数量,W为一行中为一的列下标和。
i行不翻:Val = A*i*cnt+B*W
i行翻:Val' = A*i*(m-cnt)+B*(m*(m+1)/2-W)
则我们想要 Val' > Val 才翻。
整理不等式得到条件是 A*i*(m-2*cnt)>B*(2*W-m*(m+1)/2)。可以发现不等式只和行号i以及W和cnt有关。而后两者在特定的列翻转条件下是固定的,所以我们认为会有分界线分开行号的翻转。
不同的情况分别存储行号,找到分界线用二分。我找到后,直接各种情况取max了。
话说这个map不太好,卡到990ms多,建议换数组。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl '\n'
#define fff(x,y,z) for(int x=(y);x<=(z);x++)
#define rrr(x,y,z) for(int x=(y);x>=(z);x--)
using pii = pair<int,int>;
using ari = array<int,3>;
// const int MAX;
// const int MOD;
int N,M,A,B;
pii Cal(const string&str,const int state){
// vector<bool> nstate;
int cnt=0,W=0;
for(int i=0;i<M;i++){
int cur=str[i]-'0';
if((state>>i)&1)cur=1-cur;
if(cur){
cnt++;
W+=i+1;
}
}
return{cnt,W};
}
void solve(){
cin>>N>>M>>A>>B;
map<string,vector<int>>mp;
map<string,vector<int>>mpre;
fff(i,1,N){
string str;cin>>str;
mp[str].emplace_back(i);
}
for(auto[str,temp]:mp){
int pre=0;
for(auto t:temp){
pre+=t;
mpre[str].emplace_back(pre);
}
}
int ans=LONG_MIN;
fff(state,0,(1<<M)-1){
int sans=0;
for(auto[str,temp]:mp){
int part_ans=LONG_MIN;
auto[cnt,W]=Cal(str,state);
int len=temp.size();
int Ap=A*(M-2*cnt),Bp=B*(2*W-M*(M+1)/2);
int ll=-1,rr=len;
if(Ap>0) while(ll+1!=rr){
int mid=(ll+rr)/2;
if(Ap*temp[mid]>Bp)rr=mid;
else ll=mid;
}else if(Ap<0) while(ll+1!=rr){
int mid=(ll+rr)/2;
if(Ap*temp[mid]>Bp)ll=mid;
else rr=mid;
}
if(Ap==0||ll==len-1||rr==0){
part_ans=max(part_ans,max(A*mpre[str][len-1]*cnt+len*B*W,A*mpre[str][len-1]*(M-cnt)+len*B*(M*(M+1)/2-W)));
sans+=part_ans;
continue;
}
part_ans=max(part_ans,A*mpre[str][ll]*cnt+B*W*rr+A*(mpre[str][len-1]-mpre[str][ll])*(M-cnt)+B*(M*(M+1)/2-W)*(len-rr));
part_ans=max(part_ans,A*(mpre[str][len-1]-mpre[str][ll])*cnt+B*W*(len-rr)+A*mpre[str][ll]*(M-cnt)+B*(M*(M+1)/2-W)*rr);
sans+=part_ans;
}
ans=max(sans,ans);
}
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
// int times;cin>>times;
// while(times--)
solve();
return 0;
}
K. Typewriter
题意:给你个字符串,要你求每个前缀的最短的字符串ALB(长度大于0即可),满足这个前缀是ALBLR(LR意思是L反转)无限重复的前缀。
字符串好题。复习了一下马拉车和Z函数。
对于位置i有3种情况(1-base)(长度为len)。
- len>|ALBLR|,要求ALB为str[1~i]的周期、且ALBLRA也是回文。此时答案为|ALB|
- len>|ALB| && len<=|ALBLR|,此时答案为最长回文后缀的中心到开头的字符串长度
- ALB=str[1~i],此时答案为前缀长度
对于情况1,我们要认识到,随着i不断增长,字符串的周期只能不变或变长,不会再变短。
对于情况2,我们要认识到,随着i不断增长,回文后缀的中心是不会往前移动的。
这两种情况都可以分别维护一个位置,配合遍历时的i来维护答案。
情况1要判断,从开头到维护的位置后1位的字符串是不是回文串,又要看看从开头到维护的位置能否成为周期,这些可以分别用马拉车和Z函数。
情况二判断尾部回文,马拉车即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl '\n'
#define fff(x,y,z) for(int x=(y);x<=(z);x++)
#define rrr(x,y,z) for(int x=(y);x>=(z);x--)
using pii = pair<int,int>;
using ari = array<int,3>;
// const int MAX;
// const int MOD;
int N,M;
#define ra(i) m[2*i+1]/2-1
std::vector<int> manacher(std::string s) {
std::string t = "#";
for (auto c : s) {
t += c;
t += '#';
}
int n = t.size();
std::vector<int> r(n);
for (int i = 0, j = 0; i < n; i++) {
if (2 * j - i >= 0 && j + r[j] > i) {
r[i] = std::min(r[2 * j - i], j + r[j] - i);
}
while (i - r[i] >= 0 && i + r[i] < n && t[i - r[i]] == t[i + r[i]]) {
r[i] += 1;
}
if (i + r[i] > j + r[j]) {
j = i;
}
}
return r;
}
std::vector<int> Z(std::string s) {
int n = s.size();
std::vector<int> z(n + 1);
z[0] = n;
for (int i = 1, j = 1; i < n; i++) {
z[i] = std::max(0ll, std::min(j + z[j] - i, z[i - j]));
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) {
z[i]++;
}
if (i + z[i] > j + z[j]) {
j = i;
}
}
return z;
}
void solve(){
string str;cin>>str;
int len=str.length();
vector<int> ans1(len,LONG_MAX),ans2(ans1);
// iota(ans3.begin(),ans3.end(),1);
auto z=Z(str);
auto m=manacher(str);
// cout<<"Z:";
// for(auto t:z)cout<<t<<' ';cout<<endl;
// cout<<"M:";
// for(auto t:m)cout<<t<<' ';cout<<endl;
// fff(i,0,len-1){
// int mi=2*i+1;
// int r=m[mi]/2-1;
// fff(j,i,i+r){
// ans2[j]=min(ans2[j],i+1);
// }
//// i+=r;
// }
int pos=0;
fff(i,0,len-1){
if(pos==0&&str[i]==str[0])ans1[i]=1;
else while(pos<i){
int pmid=(pos+1)/2;
// if(pos==0)
if((pos&1)&&pmid+ra(pmid)>=pos+1&&z[pos+1]>=i-pos){
// [0~pos+1]得是奇数串 这个奇数串得是回文的 [0~pos]得是[0~i]的周期
// cout<<"Y";
ans1[i]=(pos+3)/2;
break;
}
pos++;
}
// ans1[i]=(pos+3)/2;
}
int centre=0;
fff(i,0,len-1){
while(centre<i&¢re+ra(centre)<i)centre++;
ans2[i]=centre+1;
}
// cout<<"ans1:";
// for(auto t:ans1)cout<<t<<' ';cout<<endl;
// cout<<"ans2:";
// for(auto t:ans2)cout<<t<<' ';cout<<endl;
int ans=0;
fff(i,0,len-1){
ans^=((i+1)*min(min(ans1[i],ans2[i]),i+1));
}
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int times;cin>>times;
while(times--)
solve();
return 0;
}

浙公网安备 33010602011771号