Codeforces Round #646 Div. 2 May/31/2020 22:35UTC+8
比赛链接 https://codeforces.com/contest/1363
比赛记录 https://blog.youkuaiyun.com/cheng__yu_/article/details/105395197
A. Odd Selection
题意:问能否从 n 个数中取 x 个凑成奇数
思路:很简单的题,但直接分类讨论还挺麻烦的。可以循环一遍
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;
int t,n,x;
int a;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>x;
int odd=0,even=0;
for(int i=1;i<=n;++i)
{
cin>>a;
if(a&1) odd++;
else even++;
}
bool ok=0;
for(int i=1;i<=odd;i+=2)
{
if(i+even>=x&&i<=x)
ok=1;
}
puts(ok?"YES":"NO");
}
return 0;
}
分类讨论
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10,mod=998244353;
int t,n,x;
int a;
int main()
{
cin>>t;
while(t--)
{
cin>>n>>x;
int even=0,odd=0;
for(int i=1;i<=n;++i)
{
cin>>a;
if(a&1) odd++;
else even++;
}
if(odd==0)
{
puts("NO");
continue;
}
x--;
odd--;
if(x&1)
{
if(even==0)
{
puts("NO");
continue;
}
even--;
x--;
}
int tt=odd/2;
x=max(0,x-tt*2);
if(x==0)
{
puts("YES");
continue;
}
x=max(0,x-even);
if(x==0)
{
puts("YES");
continue;
}
puts("NO");
}
return 0;
}
D. Guess The Maximums(交互:二分)
题意:交互题。有一个长度为 n 的数组,有 k 个互不相交的索引的集合,让你构造一个长度为 k 的密码。在构造密码第 i 位时,需要选择数组中不在第 i 个集合内的最大值(集合外的最大值)。你可以向系统询问数组的一些索引,系统会回复你最大值。问最终的密码是多少?
思路:首先 [1,n] 询问一下最大值是多少,然后二分出最大值的位置。然后就没了
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1000+10,mod=998244353;
int t,n,k;
int sz,mx;
int visit[maxn];
vector<int> s[maxn];
int query(int n)
{
printf("? %d ",n);
for(int i=1;i<=n;++i)
printf("%d%c",i,i==n?'\n':' ');
fflush(stdout);
int res;
cin>>res;
return res;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=k;++i)
{
cin>>sz;
s[i].resize(sz);
for(auto &x : s[i])
cin>>x;
}
mx=query(n);
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(query(mid)<mx)
l=mid+1;
else
r=mid;
}
vector<int> ans;
for(int i=1;i<=k;++i)
{
bool exist=0;
for(auto j : s[i])
if(j==l)
exist=1;
if(!exist)
{
ans.push_back(mx);
continue;
}
memset(visit,0,sizeof(visit));
for(auto j : s[i])
visit[j]=1;
printf("? %d",n-s[i].size());
for(int j=1;j<=n;++j)
if(!visit[j])
printf(" %d",j);
printf("\n");
fflush(stdout);
int t;
cin>>t;
ans.push_back(t);
}
printf("! ");
for(int i=0;i<ans.size();++i)
printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
fflush(stdout);
string t;
cin>>t;
}
return 0;
}
E. Tree Shuffling(贪心)
题意:给定一棵树,每个节点有三个值 a、b、c,b和c只能是0或1。一次操作可以选择一个节点u子树上的任意k个节点,花费 a[u]*k,将它们的b值交换。求最小的花费使得每个节点 b 等于 c 。
思路:每个节点想要完成的操作都可以在父节点完成,因此每个节点 v 的 a,应该是所有父节点 a 的最小值。
- 简单的说,就是当前节点的 a 值是它到根节点路径中所有 a 的最小值。这样把最小值向下累积计算就好了。
- 不能直接算完的交给父节点计算
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10,mod=998244353;
int n;
int a[maxn],b[maxn],c[maxn];
vector<int> e[maxn];
ll ans;
int cnt[maxn][2];
void dfs(int u,int fa)
{
a[u]=min(a[u],a[fa]);
for(auto v : e[u])
{
if(v==fa) continue;
dfs(v,u);
cnt[u][0]+=cnt[v][0];
cnt[u][1]+=cnt[v][1];
}
if(b[u]!=c[u]) cnt[u][b[u]]++;
int x=min(cnt[u][0],cnt[u][1]);
ans+=2ll*a[u]*x;
cnt[u][0]-=x;
cnt[u][1]-=x;
}
int main()
{
scanf("%d",&n);
int c1=0,c2=0;
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&a[i],&b[i],&c[i]);
if(b[i]) c1++;
if(c[i]) c2++;
}
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
if(c1!=c2)
{
puts("-1");
return 0;
}
ans=0;
a[0]=2e9;
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
F. Rotating Substrings(DP)
题意:给定两个字符串 s 和 t ,每次可以选择一个 l 、r,将 s[ r ] 插入到 s[ l ] 前面。问最少需要操作多少次可以使得 s 和 t 相等
思路:
-
一种思路是用 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示当前在 s 的第 i 个位置,t 的第 j 个位置的最长公共子序列的个数。同时 i <=j,并且 s [1…i ] 是 t [ 1…j ] 的子序列。只有是子序列,才能够在 s[i+1…n]中取字符插入到前面和 t 相等。最后的答案就是, n − d p [ n ] [ n ] n -dp[n][n] n−dp[n][n]
当 s [ i ] = t [ j ] s[i]=t[j] s[i]=t[j]时,并且满足 s [1…i ] 是 t [ 1…j ] 的子序列时,则可以转移 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − 1 ] + 1 , d p [ i ] [ j ] ) dp[i][j]=max(dp[i-1][j-1]+1,dp[i][j]) dp[i][j]=max(dp[i−1][j−1]+1,dp[i][j])
当 s [ i ] ! = t [ j ] s[i]!=t[j] s[i]!=t[j] 时, d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j]=max(dp[i][j],dp[i-1][j],dp[i][j-1]) dp[i][j]=max(dp[i][j],dp[i−1][j],dp[i][j−1]) -
另一种是题解的思路,用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示匹配了 s 中 i 个字符,匹配到 t 中第 j 个字符所需要的最少插入数
当 s [ i ] = t [ j ] s[i]=t[j] s[i]=t[j] 时, d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i ] [ j ] ) dp[i][j]=min(dp[i-1][j-1],dp[i][j]) dp[i][j]=min(dp[i−1][j−1],dp[i][j])
当 s [ i ] ! = t [ j ] s[i]!=t[j] s[i]!=t[j] 时, d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j]=dp[i][j-1] dp[i][j]=dp[i][j−1]但是需要满足的条件是当前匹配的字符是t[j],那么在后面的 s [ i + 1 … n ] s[i+1…n] s[i+1…n] 中 t [ j ] t[j] t[j] 的个数要大于 t [ j + 1 … n ] t[j+1…n] t[j+1…n] 中 t [ j ] t[j] t[j] 的个数
d p [ i ] [ j ] dp[i][j] dp[i][j]也可以用 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]通过从后面找到字符 t [ j ] t[j] t[j] 插入到前面来转移,也就是 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + 1 dp[i][j]=dp[i-1][j]+1 dp[i][j]=dp[i−1][j]+1
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;
int tt,n;
int dp[maxn][maxn];
string s,t;
int main()
{
cin>>tt;
while(tt--)
{
cin>>n>>s>>t;
string s1=s,t1=t;
sort(s1.begin(),s1.end());
sort(t1.begin(),t1.end());
if(s1!=t1)
{
puts("-1");
continue;
}
vector<vector<int> > nums(n+1,vector<int>(27)),numt(n+1,vector<int>(27));
for(int i=1;i<=n;++i)
{
nums[i]=nums[i-1];
numt[i]=numt[i-1];
nums[i][s[i-1]-'a'+1]++;
numt[i][t[i-1]-'a'+1]++;
}
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
dp[i][j]=-10000;
dp[0][0]=0;
for(int i=0;i<=n;++i)
{
for(int j=0;j<=n;++j)
{
if(i>=1) dp[i][j]=max(dp[i][j],dp[i-1][j]);
if(j>=1) dp[i][j]=max(dp[i][j],dp[i][j-1]);
if(i>=1&&j>=1&&i<=j&&s[i-1]==t[j-1])
{
bool ok=1;
for(int k=1;k<=26;++k)
if(nums[i][k]>numt[j][k])
ok=0;
if(ok) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
}
}
cout<<n-dp[n][n]<<"\n";
}
return 0;
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;
int tt,n;
int dp[maxn][maxn];
string s,t;
int sums[30][maxn],sumt[30][maxn];
int main()
{
cin>>tt;
while(tt--)
{
cin>>n>>s>>t;
string s1=s,t1=t;
sort(s1.begin(),s1.end());
sort(t1.begin(),t1.end());
if(s1!=t1)
{
puts("-1");
continue;
}
memset(sums,0,sizeof(sums));
memset(sumt,0,sizeof(sumt));
for(int i=1;i<=n;++i)
{
sums[s[i-1]-'a'+1][i]++;
sumt[t[i-1]-'a'+1][i]++;
}
for(int i=1;i<=26;++i)
for(int j=1;j<=n;++j)
sums[i][j]+=sums[i][j-1],sumt[i][j]+=sumt[i][j-1];
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
dp[i][j]=10000;
for(int i=0;i<=n;++i)
dp[0][i]=0;
for(int i=1;i<=n;++i)
{
for(int j=i;j<=n;++j)
{
if(s[i-1]==t[j-1])
dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
else
{
dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
int c=t[j-1]-'a'+1;
if(sums[c][i]<sumt[c][j])
dp[i][j]=min(dp[i][j],dp[i][j-1]);
}
}
}
cout<<dp[n][n]<<"\n";
}
return 0;
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000+10,mod=998244353;
int tt,n;
int dp[maxn][maxn];
string s,t;
int main()
{
cin>>tt;
while(tt--)
{
cin>>n>>s>>t;
string s1=s,t1=t;
sort(s1.begin(),s1.end());
sort(t1.begin(),t1.end());
if(s1!=t1)
{
puts("-1");
continue;
}
vector<vector<int> > nums(n+1,vector<int>(27)),numt(n+1,vector<int>(27));
for(int i=1;i<=n;++i)
{
nums[i]=nums[i-1];
numt[i]=numt[i-1];
nums[i][s[i-1]-'a'+1]++;
numt[i][t[i-1]-'a'+1]++;
}
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
dp[i][j]=10000;
for(int i=0;i<=n;++i)
dp[0][i]=0;
for(int i=1;i<=n;++i)
{
for(int j=i;j<=n;++j)
{
if(s[i-1]==t[j-1])
dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
else
{
dp[i][j]=min(dp[i][j],dp[i-1][j]+1);
int c=t[j-1]-'a'+1;
if(nums[i][c]<numt[j][c])
dp[i][j]=min(dp[i][j],dp[i][j-1]);
}
}
}
cout<<dp[n][n]<<"\n";
}
return 0;
}