CF1927 A~G
庆祝一下全写的正解。
赛时 1h A~E,罚坐 1h,以为 dfs 找环复杂度巨大导致没有 F。
A
找最左边和最右边的'B'即可,注意找不到时的处理。
#include<bits/stdc++.h>
#define int long long
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
signed main(){
int t;
read(t);
while(t--){
int n;
read(n);
int l,r;
l=-1,r=-1;
for(int i=1;i<=n;i++){
char c=getchar();
if(c=='B'){
if(l==-1) l=i,r=i;
else r=i;
}
}
printf("%lld\n",std::max(r-l+1,0ll));
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
B
用 \(26\) 个桶记录一下每一个字母出现的次数,不断找合法字母即可,时间复杂度 \(O(26n)\)。
#include<bits/stdc++.h>
#define int long long
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
signed main(){
int t;
read(t);
while(t--){
int n;
read(n);
int t[30]={0};
for(int i=1;i<=n;i++){
int g;
read(g);
int flag=0;
for(int j=1;j<=26;j++){
if(g==t[j]) {
t[j]++;
flag=j;
break;
}
}
printf("%c",(char)('a'+flag-1));
}
printf("\n");
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
C
明显的贪心,记录一下每个数字在哪个数组中出现过,统计一下每个数组有多少只在自己数组出现的数,如果这个数超过 \(k\) 或有数不在两个数组中出现过,则代表无解,否则一定有解。
读者自证不难。
#include<bits/stdc++.h>
#define int long long
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
const int maxn=4e5+10;
int a[maxn],b[maxn],t1[maxn],t2[maxn];
struct node{
int fi,se,th;
};
signed main(){
int t;
read(t);
while(t--){
int n,m,k;
read(n),read(m),read(k);
for(int i=1;i<=k;i++) t1[i]=t2[i]=0;
for(int i=1;i<=n;i++) {
read(a[i]);
t1[a[i]]++;
}
for(int i=1;i<=m;i++) {
read(b[i]);
t2[b[i]]++;
}
int s1,s2;
s1=s2=0;
bool flag=1;
for(int i=1;i<=k;i++){
// printf("&&%lld %lld %lld\n",i,t1[i],t2[i]);
if(t1[i]&&t2[i]) continue;
if(t1[i]) s1++;
else if(t2[i]) s2++;
else {
flag=0;
break;
}
if(s1>k/2||s2>k/2){
flag=0;
break;
}
}
if(flag) printf("YES\n");
else printf("NO\n");
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
D
经典套路,维护一下每个数后面第一个和它不一样的数即可,这可以从后向前递推得到。
#include<bits/stdc++.h>
#define int long long
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
const int maxn=2e5+10;
int a[maxn],to[maxn];
namespace bcj{
void init(int n){
for(int i=1;i<=n+1;i++) to[i]=i;
}
int fa(int p){
if(to[p]==p) return p;
return to[p]=fa(to[p]);
}
}
signed main(){
int t;
read(t);
while(t--){
int n;
read(n);
for(int i=1;i<=n;i++) read(a[i]);
to[n]=n+1;
for(int i=n-1;i>=1;i--){
if(a[i]==a[i+1]) to[i]=to[i+1];
else to[i]=i+1;
}
int q;
read(q);
while(q--){
int l,r;
read(l),read(r);
if(to[l]>r) printf("-1 -1\n");
else printf("%lld %lld\n",l,to[l]);
}
printf("\n");
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
E
考虑一个长为 \(k\) 个窗口,如果将其往后滑动一格,可以发现 \(a_1+a_2+\ldots+a_k\rightarrow a_2+a_3+a\ldots+a_{k+1}\),将两者作差发现 \(\operatorname{delta}=a_{k+1}-a_1\),根据题意,\(|a_{k+1}-a_1|\le 1\)。同理,继续往后移,发现 \(|a_{k+2}-a_2|\le 1\)。继续综合题目的限制,有 \(|a_{k+1}+a_{k+2}-a_1-a_2|/le 1\),因为不存在 \(a_{k+1}=a_1\),所以 \(a_{k+1}-a_1=1/-1\),综合这三个式子:
-
\(a_{k+1}-a_1=1/-1\)
-
\(a_{k+2}-a_2=1/-1\)
-
\(|(a_{k+1}-a_1)+(a_{k+2}-a_2)|/le 1\)
发现 \(a_{k+1}-a_1+a_{k+2}-a_2=0\),因此两者一定一个为 \(1\),一个为 \(-1\)。
根据数学归纳法,发现若 \(a_{k+x}-a_x=1\),则 \(a_{k+x+1}-a_{x+1}=-1\),于是我们可以按照如下方法构造:
令 \(a_1=n,a_2=1\),则 \(a_{k+1}=n-1,a),a_{k+2}=2\),依次类推,每次往后跳 \(k\) 步,直到跳出去。
具体可以看代码实现。
#include<bits/stdc++.h>
#define int long long
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
const int maxn=2e5+10;
int a[maxn];
signed main(){
int t;
read(t);
while(t--){
int n,k;
read(n),read(k);
int r=n+1,l=0,k_=k;
if(k&1) k_=k+1;
for(int i=1;i<=k;i++){
if(i&1) a[i]=--r;
else a[i]=++l;
int z=i+k_;
while(z<=n){
if(i&1)a[z]=--r;
else a[z]=++l;
z+=k_;
}
}
for(int i=1;i<=n;i++) printf("%lld ",a[i]);
printf("\n");
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
F
利用生成树的思想,从大到小往里面加边,如果发现成环,则更新最小答案(加边顺序使得新答案一定小于原来),即为答案。
至于寻找简单环,可以利用 dfs 来找环,注意实现的细节,一定要加上记忆化的技巧。
#include<bits/stdc++.h>
#define int long long
#define ok printf("ORz\n")
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
const int maxm=4e5+10;
struct node{
int nxt,to,v;
}s[maxm];
int fi[maxm];
struct node2{
int u,v,w;
}p[maxm];
int be=0,tot=0,t;
bool cmp(node2 s1,node2 s2){
return s1.w>s2.w;
}
int to[maxm],minans;
int go(int p){
if(to[p]==p) return p;
return to[p]=go(to[p]);
}
struct node3{
int to,nxt,v;
}p2[maxm];
int F[maxm];
int tot2;
std::vector<int>ans[(int)1e4+10],mans;
bool vis[maxm],mflag=1;
void dfs(int now,int mb,bool flag){
if(!mflag) return ;
// printf("@@@@@%lld\n",now);
// if(now==mb&&flag){
// mans=ans;
// mans.pop_back();
// return ;
// }
int zz=F[now];
while(zz){
if(!mflag) return ;
int to=p2[zz].to,nxt=p2[zz].nxt;
if(vis[to]) {
if(to==mb&&ans[t].size()>2) {
mflag=0;
// ans[t].pop_back();
// mans=ans[t];
// printf("##%lld %lld\n",mans.size(),ans[t].size());
return ;
}
else {
zz=nxt;
continue;
}
}
vis[to]=1,ans[t].push_back(to);
dfs(to,mb,1);
if(mflag) ans[t].pop_back();
zz=nxt;
}
return ;
}
signed main(){
// int t;
read(t);
while(t--){
int n,m;
read(n),read(m);
int tot=0;
for(int i=1;i<=m;i++){
int u,v,w;
read(u),read(v),read(w);
p[i].u=u,p[i].v=v,p[i].w=w;
}
std::sort(p+1,p+m+1,cmp);
int fi=-1,se,vv;
int tot3=0;
for(int i=1;i<=n;i++) F[i]=0,vis[i]=0,to[i]=i;
for(int i=1;i<=m;i++){
int u=p[i].u,v=p[i].v,w=p[i].w;
if(go(u)==go(v)) {
fi=u,se=v,vv=w;
}
else to[go(u)]=go(v);
}
for(int i=1;i<=m;i++){
int u=p[i].u,v=p[i].v,w=p[i].w;
tot3++;
// F[u]=tot3;
p2[tot3].to=v,p2[tot3].nxt=F[u],F[u]=tot3;
tot3++;
p2[tot3].to=u,p2[tot3].nxt=F[v],F[v]=tot3;
}
// for(int i=1;i<=n;i++) vis[i]=0;
printf("%lld ",vv);
// vis[fi]=1;
minans=inf;
// ans.shirnk_to_fit();
// std::vector<int>ans;
ans[t].push_back(fi);
vis[fi]=2;
mflag=1;
dfs(fi,fi,0);
printf("%lld\n",ans[t].size());
for(int i:ans[t]) printf("%lld ",i);
printf("\n");
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
G
非常巧妙的一道区间 dp。
令 \(dp_{i,j,k}\) 表示考虑完前 \(i\) 个点,未被染色的点中最左边的一个为 \(j\),被染色的点中最右边的一个为 \(k\)。
先说转移。这里尽量采用递推法。分别讨论第 \(i+1\) 个点不染/向左染/向右染。明显,不染的情况可以得到 \(dp_{i,j,k}\rightarrow dp_{i,j,k}\);对于向左/右染的情况,我们更新 \(j,k\),具体实现看代码。
可以证明,这样转移会得到正确答案,读者自证不难。
现在我们考虑,为什么不能设置状态为被染色的点中最左边和最右边的分别为 \(j,k\)。如果这么设置,会出现 \(j\sim k\) 并不是一段连续的染色段,或许会出现零星的未染色段;而对于正确的状态设置方法,则会避免在这种情况下出现错误。
关于如何在考场上想到这个做法,或许要凭 OI 上的直觉了。
#include<bits/stdc++.h>
#define int long long
template<typename T>
void read(T &x){
int f=1;
char c=getchar();
x=0;
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') x=x*10+(int)(c-'0'),c=getchar();
x*=f;
}
template<typename T,typename I>
void chkmin(T &a,I b){
a=std::min(a,b);
}
template<typename T,typename I>
void chkmax(T &a,T b){
a=std::max(a,b);
}
const int inf=1e18+10,MOD1=998244353,MOD2=1e9+7;
const int maxn=110;
int a[maxn],dp[maxn][maxn][maxn],q[maxn],h[maxn];
signed main(){
int t;
read(t);
while(t--){
int n;
read(n);
for(int i=1;i<=n;i++) read(a[i]),q[i]=std::max(i-a[i]+1,1ll),h[i]=std::min(i+a[i]-1,n);
memset(dp,0x3f,sizeof(dp));
dp[0][1][0]=0;
for(int i=0;i<n;i++){
for(int j=1;j<=n+1;j++)
for(int k=0;k<=n;k++) chkmin(dp[i+1][j][k],dp[i][j][k]);
for(int j=1;j<=n+1;j++)
for(int k=0;k<=n;k++){
if(q[i+1]<=j) chkmin(dp[i+1][std::max(k+1,i+2)][std::max(k,i+1)],dp[i][j][k]+1);
else chkmin(dp[i+1][j][std::max(k,i+1)],dp[i][j][k]+1);
}
for(int j=1;j<=n+1;j++)
for(int k=0;k<=n;k++){
if(j>=i+1) chkmin(dp[i+1][std::max(j,h[i+1]+1)][std::max(h[i+1],k)],dp[i][j][k]+1);
else chkmin(dp[i+1][j][std::max(h[i+1],k)],dp[i][j][k]+1);
}
}
printf("%lld\n",dp[n][n+1][n]);
}
return 0;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/