前两题一直在打模拟赛,有点忙,就没更
Red Playing Cards
算法:动态规划
其实这就是一个线段覆盖问题,只不过大线段能够包含小线段。
这就启发我们,对于每个大线段分别跑一个dp,合并在他内部的小线段。而后对于每一个大线段,再跑一个总的dp即可。
也可以只跑一遍dp,有一个小trick,在线段两端添0,这样答案就等同于f[0]
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 6e3+100;
int n;
int f[N];
int l[N],r[N];
int a[N];
struct Node{
int l,r,x;
}b[N];
int g[N];
int dp[N];
bool cmp(Node x,Node y){
int l1 = x.r-x.l+1;
int l2 = y.r-y.l+1;
return l1 < l2;
}
signed main(){
cin>>n;
for (int i = 1; i <= 2*n; i++){
cin>>a[i];
if (l[a[i]] == 0) l[a[i]] = i;
else r[a[i]] = i;
}
for (int i = 1; i <= n; i++)
b[i] = {l[i],r[i],i};
sort(b+1,b+n+1,cmp);
for (int i = 1; i <= n; i++){
int x = b[i].x;
for (int j = 1; j <= 2*n; j++) g[j] = 0;
for (int j = b[i].l; j <= b[i].r; j++){
g[j] = g[j-1]+x;
if (l[a[j]] < j && l[a[j]] > b[i].l)
g[j] = max(g[l[a[j]]-1]+f[a[j]],g[j]);
}
f[x] = g[b[i].r];
}
for (int i = 1; i <= 2*n; i++){
dp[i] = max(dp[i-1],dp[i]);
if (l[a[i]] == i) continue;
dp[i] = max(dp[i],dp[l[a[i]]-1]+f[a[i]]);
}
cout<<dp[2*n];
return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 6e3+100;
int n;
int f[N];
int l[N],r[N];
int a[N];
struct Node{
int l,r,x;
}b[N];
int g[N];
int dp[N];
bool cmp(Node x,Node y){
int l1 = x.r-x.l+1;
int l2 = y.r-y.l+1;
return l1 < l2;
}
signed main(){
cin>>n;
for (int i = 2; i <= 2*n+1; i++){
cin>>a[i];
if (l[a[i]] == 0) l[a[i]] = i;
else r[a[i]] = i;
}
for (int i = 1; i <= n; i++)
b[i] = {l[i],r[i],i};
b[n+1] = {1,2*n+2,0}; ++n;
sort(b+1,b+n+1,cmp);
for (int i = 1; i <= n; i++){
int x = b[i].x;
for (int j = 0; j <= 2*n; j++) g[j] = 0;
for (int j = b[i].l; j <= b[i].r; j++){
g[j] = g[j-1]+x;
if (l[a[j]] < j && l[a[j]] > b[i].l)
g[j] = max(g[l[a[j]]-1]+f[a[j]],g[j]);
}
f[x] = g[b[i].r];
}
cout<<f[0];
// for (int i = 1; i <= 2*n; i++){
// dp[i] = max(dp[i-1],dp[i]);
// if (l[a[i]] == i) continue;
// dp[i] = max(dp[i],dp[l[a[i]]-1]+f[a[i]]);
// }
// cout<<dp[2*n];
// for (int i = 1; i <= n; i++) cout<<"i = "<<f[i]<<endl;
return 0;
}
Lucky Common Subsequence
算法:KMPdp
这题只是一个加强版的LCS,只不过多了一个子串的限定。
所以我们不难想到状态设置:
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k]表示第一个串从1……i,第二个串从2……j,匹配了第三个串k个长度的最长LCS
关键就是第三维状态的转移,我们不能随便转移,而是利用KMP的NEXT数组进行转移
在第三个串的k位之后加入s[i],利用NEXT数组转移到相应的位置进行转移。
由于本题要求输出路径,有两种方法
第一种就是常规的求最长长度,而后利用状态关系倒序递归输出。
第二种就是直接用string去存储答案。
这里用的第二种方法
#include<bits/stdc++.h>
using namespace std;
const int N = 1e2+10;
string f[N][N][N];
int n,m,q;
char s1[N],s2[N],s3[N];
int Ne[N];
void KMP(){
Ne[1] = 0;
int j = 0;
for (int i = 2; i <= q; i++){
while (j > 0 && s3[i]!=s3[j+1]) j=Ne[j];
if (s3[i] == s3[j+1]) j++;
Ne[i] = j;
}
}
void Com(string &a,string b){
if (a.size() < b.size()) a = b;
}
int main(){
cin>>(s1+1); cin>>(s2+1); cin>>(s3+1);
n = strlen(s1+1); m = strlen(s2+1); q = strlen(s3+1);
KMP();
for (int i = 1; i <= n; i++){
for (int j = 1; j <= m; j++){
for (int k = 0; k < q; k++){
Com(f[i][j][k],f[i-1][j][k]);
Com(f[i][j][k],f[i][j-1][k]);
if (s1[i]!=s2[j]) continue;
int now = k;
while (now && s1[i]!=s3[now+1]) now = Ne[now];
if (s1[i] == s3[now+1]) now++;
Com(f[i][j][now],f[i-1][j-1][k]+s1[i]);
}
}
}
string ans = "";
for (int i = 0; i < q; i++)
Com(ans,f[n][m][i]);
if (ans.size() == 0) cout<<0; else cout<<ans;
return 0;
}
算是一个比较典的KMPdp的题目
树
启发式合并
具体的等一下再说
#include<iostream>
#include<vector>
using namespace std;
#define ull unsigned long long
#define ll long long
const int N = 5e5+100;
const int M = 1e6+100;
typedef pair < ll , ll > pii;
#define mp make_pair
#define pb push_back
#define fi first
#define se second
vector < int > a[N];
int n;
ll v[N];
int son[N];
ll s1[N],s2[N],sz[N];
//s1 he s2 pingfang
void Dfs(int x,int faa){
for (int i = 0; i < a[x].size(); i++){
int y = a[x][i];
if (y == faa) continue;
Dfs(y,x);
if (sz[y] > sz[son[x]]) son[x] = y;
s1[x]+=s1[y]; s2[x]+=s2[y]; sz[x]+=sz[y];
}
sz[x]++;
s1[x]+=v[x];
s2[x]+=(v[x]*v[x]);
return;
}
ll ans[N];
ll vl[M],num[M];
#define lowbit(x) (x&(-x))
void Change(int x,ll x1,ll x2){
for (int i = x; i < M; i+=lowbit(i))
vl[i]+=x1 , num[i]+=x2;
}
pii Ask(int x){
ll x1 = 0 , x2 = 0;
for (int i = x; i; i-=(i&(-i)))
x1+=vl[i],x2+=num[i];
return mp(x1,x2);
}
void Calc(int x,int faa,vector < ll >& now, ll &an){
now.pb(v[x]);
pii l = Ask(v[x]) , r = Ask(M-1);
an+=(l.se*v[x]*v[x]-l.fi)+((r.fi-l.fi)-(r.se-l.se)*v[x]*v[x]);
for (int i = 0; i < a[x].size(); i++){
int y = a[x][i]; if (y == faa) continue;
Calc(y,x,now,an);
}
return;
}
void Del(int x,int faa){
Change(v[x],-v[x]*v[x],-1);
for (int i = 0; i < a[x].size(); i++){
int y = a[x][i]; if (y == faa) continue;
Del(y,x);
}
}
void Dsu(int x,int faa,int op){
int so = son[x];
for (int i = 0; i < a[x].size(); i++){
int y = a[x][i];
if (y == faa || y == so) continue;
Dsu(y,x,0);
ans[x]+=ans[y];
}
if (so){
// cout<<"OK";
Dsu(so,x,1);
ans[x]+=ans[so];
}
// 接下来计算子树之间的贡献
for (int i = 0; i < a[x].size(); i++){
int y = a[x][i]; if (y == faa || y == so) continue;
vector < ll > now;
Calc(y,x,now,ans[x]);
for (int j = 0; j < now.size(); j++) Change(now[j],now[j]*now[j],1);
}
pii l = Ask(v[x]) , r = Ask(M-1);
ans[x]+=(l.se*v[x]*v[x]-l.fi)+((r.fi-l.fi)-(r.se-l.se)*v[x]*v[x]);
Change(v[x],v[x]*v[x],1);
if (op == 0) Del(x,faa);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for (int i = 1,x,y; i < n; i++)
cin>>x>>y,a[x].pb(y),a[y].pb(x);
for (int i = 1; i <= n; i++) cin>>v[i];
Dfs(1,0);
Dsu(1,0,0);
ull An = 0;
for (int i = 1; i <= n; i++){
ull x = sz[i]*s2[i],y = s1[i]*s1[i];
x = x+ans[i];
x = (x-y);
An^=x;
}
cout<<An<<endl;
return 0;
}
Ciel and Duel
贪心
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 500;
int n,m;
int at[N],de[N];
int a[N];
int n1,n2;
int Do1(){
int ans = 0;
int now = m;
for (int i = 1; i <= n1; i++){
if (now == 0) break;
if (a[now] >= at[i]) ans+=(a[now]-at[i]),now--;
else break;
}
return ans;
}
bool vi[N];
int b[N],cnt=0;
int Do2(){
int now = 1;
for (int i = 1; i <= n2; i++){
while (now <= m && a[now] <= de[i]) now++;
vi[now] = 1; now++;
}
if (now > m) return 0;
for (int i = 1; i <= m; i++) if (vi[i] == 0) b[++cnt] = a[i];
now = 1;
int ans = 0;
bool f = 1;
memset(vi,0,sizeof vi);
for (int i = 1; i <= n1; i++){
while (now <= cnt && b[now] < at[i]) now++;
if (now > cnt) return ans;
ans+=(b[now]-at[i]);
vi[now++] = 1;
}
for (int i = 1; i <= cnt; i++) if (vi[i] == 0) ans+=b[i];
return ans;
}
signed main(){
cin>>n>>m;
memset(de,-1,sizeof de); memset(at,-1,sizeof at);
memset(a,-1,sizeof a); memset(b,-1,sizeof b);
for (int i = 1; i <= n; i++){
string s; int x;
cin>>s>>x;
if (s == "ATK") at[++n1] = x;
else de[++n2] = x;
}
for (int i = 1; i <= m; i++) cin>>a[i];
sort(at+1,at+n1+1); sort(de+1,de+n2+1);sort(a+1,a+m+1);
int Max = max(Do1(),Do2());
cout<<Max<<endl;
return 0;
}