CF1927 A~G

庆祝一下全写的正解。

赛时 1h A~E,罚坐 1h,以为 dfs 找环复杂度巨大导致没有 F。

A

link

找最左边和最右边的'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

link

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

link

明显的贪心,记录一下每个数字在哪个数组中出现过,统计一下每个数组有多少只在自己数组出现的数,如果这个数超过 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

link

经典套路,维护一下每个数后面第一个和它不一样的数即可,这可以从后向前递推得到。

#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

link

考虑一个长为 k 个窗口,如果将其往后滑动一格,可以发现 a1+a2++aka2+a3+a+ak+1,将两者作差发现 delta=ak+1a1,根据题意,|ak+1a1|1。同理,继续往后移,发现 |ak+2a2|1。继续综合题目的限制,有 |ak+1+ak+2a1a2|/le1,因为不存在 ak+1=a1,所以 ak+1a1=1/1,综合这三个式子:

  • ak+1a1=1/1

  • ak+2a2=1/1

  • |(ak+1a1)+(ak+2a2)|/le1

发现 ak+1a1+ak+2a2=0,因此两者一定一个为 1,一个为 1

根据数学归纳法,发现若 ak+xax=1,则 ak+x+1ax+1=1,于是我们可以按照如下方法构造:

a1=n,a2=1,则 ak+1=n1,a),ak+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

link

利用生成树的思想,从大到小往里面加边,如果发现成环,则更新最小答案(加边顺序使得新答案一定小于原来),即为答案。

至于寻找简单环,可以利用 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

link

非常巧妙的一道区间 dp。

dpi,j,k 表示考虑完前 i 个点,未被染色的点中最左边的一个j被染色的点中最右边的一个k

先说转移。这里尽量采用递推法。分别讨论第 i+1 个点不染/向左染/向右染。明显,不染的情况可以得到 dpi,j,kdpi,j,k;对于向左/右染的情况,我们更新 j,k,具体实现看代码。

可以证明,这样转移会得到正确答案,读者自证不难。

现在我们考虑,为什么不能设置状态为被染色的点中最左边和最右边的分别为 j,k。如果这么设置,会出现 jk 并不是一段连续的染色段,或许会出现零星的未染色段;而对于正确的状态设置方法,则会避免在这种情况下出现错误。

关于如何在考场上想到这个做法,或许要凭 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;
}
/*
-读入字符一定检查回车
- 能不能搜索?
-函数要有返回值!
-想好了再写!
*/
posted @   BYR_KKK  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示