AtCoder 题目集2
AtCoder 题目集2
终于迈入了一个新的阶段,接下来希望质量能高一点吧。
现在我主要刷的是1600左右的题,毕竟实力太拉,只能按照 ”分上200“ 的策略。
(我觉得类型标个 “思维” 貌似没啥意义,毕竟AT几乎全是思维啊...)
编号(NO.) | 题目 | 难度 | 类型 |
---|---|---|---|
1 | ABC201E | 1694,medium | 思维,XOR |
2 | ABC243E | 1637,medium | 最短路 |
3 | ABC083D | 1646,hard- | 思维 |
4 | ABC092D | 1445,hard- | 构造 |
5 | ABC081D | 1567,hard- | 前缀和 |
6 | ABC185E | 1468,medium | DP |
7 | ARC102D | 1810,easy | 构造 |
8 | AGC016B | 1626,easy | 分类讨论,思维 |
9 | |||
10 |
NO.1 ABC201E
有关 \(XOR\) 的题目很多都考察了按位解决的思想...确实,很常见的一种解决办法。
所以对于这道题,只要能发现按位解决就OK了。
最后复杂度(我写的) 是 \(O(60n)\) 左右...但是慢的一比...不知道为啥...
const int N=200010,mod=1e9+7;
int n,cnt,head[N];
struct Edge{
int to,nxt; ll val;
}edge[N<<1];
void Add(int fo,int to,ll val){
edge[++cnt]={to,head[fo],val};
head[fo]=cnt;
}
ll f[N][60],ans,sz[N];
void dfs(int pos,int fa,ll dis){
sz[pos]=1;
for(int i=0;i<60;++i) f[pos][i]=!!(dis&(1ll<<i));
for(int i=head[pos];i;i=edge[i].nxt){
int &to=edge[i].to;
if(to^fa){
dfs(to,pos,dis^edge[i].val);
for(int i=0;i<60;++i)
f[pos][i]+=f[to][i];
sz[pos]+=sz[to];
}
}
}
void solve(int pos,int fa,int wei,bool tag){
if(tag) ans=(ans+(sz[1]-f[1][wei])*((1ll<<wei)%mod)%mod)%mod;
else ans=(ans+f[1][wei]*((1ll<<wei)%mod)%mod)%mod;
for(int i=head[pos];i;i=edge[i].nxt){
int &to=edge[i].to;
if(to^fa){
if(edge[i].val&(1ll<<wei))
solve(to,pos,wei,tag^1);
else solve(to,pos,wei,tag);
}
}
}
void check(int wei){
solve(1,1,wei,0);
}
signed main(){
// freopen("gen.txt","r",stdin);
// freopen("");
IOS
//int T;
cin>>n;
for(int i=1;i<n;++i){
int fo,to; ll val;
cin>>fo>>to>>val;
Add(fo,to,val);
Add(to,fo,val);
}
dfs(1,1,0);
for(int i=0;i<60;++i)
check(i);
cout<<ans*q_Pow(2,mod-2,mod)%mod<<'\n';
return 0;
}
NO.2 ABC243E
看到 \(n\leqslant 300\) 就知道先来个 \(Floyd\) 。然后对于一条边,如果可以丢掉它,那当且仅当我们任何一条最短路都不会经过它,更进一步地说,就是存在另一条路,使得路径长度不长与这条边。思路就是这样。开始我想了求最短路的时候维护一下每条边是否会使用,但是没法排除等长的情况,会少统计...
const int N=305;
int n,m;
ll dis[N][N];
struct Edge{int fo,to,val;}edge[N*N];
signed main(){
// freopen("gen.txt","r",stdin);
// freopen("a.txt","w",stdout);
IOS
//int T;
cin>>n>>m; memset(dis,0x3f3f,sizeof(dis));
for(int i=1,fo,to,val;i<=m;++i){
cin>>fo>>to>>val;
edge[i]={fo,to,val};
dis[fo][to]=dis[to][fo]=val;
}
F(1,i,1,n) F(1,j,1,n) F(1,k,1,n)
dis[j][k]=Min(dis[j][k],dis[j][i]+dis[i][k]);
int ans=0;
F(1,i,1,m){
int fo=edge[i].fo,to=edge[i].to,val=edge[i].val;
for(int j=1;j<=n;++j)
if(j!=fo&&j!=to&&dis[fo][to]>=dis[fo][j]+dis[j][to]){
++ans;
break;
}
}
cout<<ans<<'\n';
return 0;
}
NO.3 ABC083D
这题真的很烧我的脑(😄)...一直不是很明白...
可以发现,实际上 \(0/1\) 是没有区别的,可以先把 \(0\) 变成 \(1\) ,也可以反着,所以主要是要把所有不同的数变成相同的。那么对于第 \(i/(i+1)\) 这两个位置,如果字符不相同,那么一定要有个操作去把 \((i+1)\) 变为 \(i\) 或者 \(i\) 变为 \((i+1)\) ,而既然求的是最长区间,所以要么就先把 \(i+1 \rightarrow n\) 变,再 \(i \rightarrow n\) 变(也可以1~i-1这么搞),如此 \(i\) 位就变了,当然也可以变 \(j\) 位。所以只需要对每个不同相邻点对进行求min(max)即可。因为假设最后求出的长度为 \(k\) ,那么对于任意一对相邻不同点对,一定可以化为相同。
可以具体举个例子: \(1100100\) ,此时对于第四位 \(0\) 有最小 \(k=3\)
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
string s;
cin>>s; int ans=s.length();
for(int i=1;i<s.length();++i)
if(s[i]!=s[i-1]) ans=min(ans,max(i,(int)s.length()-i));
cout<<ans<<'\n';
return 0;
}
NO.4 ABC092D
实际上答案很直接,但是上来确实不好想到...(太菜)
直接搞100*100,然后上半为黑,下半为白,然后在白的里面跳着插黑,黑里插白
😄
const int N=105;
int a,b,mp[N][N];
signed main(){
IOS
cin>>a>>b;
for(int i=1;i<=50;++i) for(int j=1;j<=100;++j)
mp[i][j]=1;//black
for(int i=51;i<=100;++i) for(int j=1;j<=100;++j)
mp[i][j]=0;//white
--a,--b;
int x=1,y=1;
while(a--){
mp[x][y]=0;
y+=2;
if(y>100) x+=2,y=1;
}
x=100,y=100;
while(b--){
mp[x][y]=1;
y-=2;
if(!y) x-=2,y=100;
}
cout<<100<<' '<<100<<'\n';
for(int i=1;i<=100;++i){
for(int j=1;j<=100;++j)
cout<<(mp[i][j]?"#":".");
cout<<'\n';
}
return 0;
}
NO.5 ABC081D
发现如果全是正数,直接跑一边前缀和即可,负数则后缀和,所以先看最大值和最小值,所有数变为同号。
const int N=55;
int n,a[N];
vector<pair<int,int>> ans;
signed main(){
//freopen();
//freopen();
IOS
//int T;
cin>>n;
int mn=1e6,mx=-1e6,pmx,pmn;
for(int i=1;i<=n;++i){
cin>>a[i],mx=Max(mx,a[i]),mn=Min(mn,a[i]);
if(a[i]==mx) pmx=i;
if(a[i]==mn) pmn=i;
}
if(mx<=0){
for(int i=n;i>1;--i)
ans.push_back(make_pair(i,i-1));
} else if(mn>=0){
for(int i=2;i<=n;++i)
ans.push_back(make_pair(i-1,i));
} else{
if(mx+mn>=0){
for(int i=1;i<=n;++i) if(a[i]!=mx){
a[i]+=mx;
ans.push_back(make_pair(pmx,i));
}
for(int i=2;i<=n;++i)
ans.push_back(make_pair(i-1,i));
} else{
for(int i=1;i<=n;++i) if(a[i]!=mn){
a[i]+=mn;
ans.push_back(make_pair(pmn,i));
}
for(int i=n;i>1;--i)
ans.push_back(make_pair(i,i-1));
}
}
cout<<ans.size()<<'\n';
for(auto x:ans){
cout<<x.first<<' '<<x.second<<'\n';
}
return 0;
}
NO.6 ABC185E
令 \(f_{i,j}\) 表示 \(a\) 中前 \(i\),\(b\) 中前 \(j\) 最优。
则只有三种情况:
- 去 \(a_i\)
- 去 \(b_j\)
- 都不去
const int N=1005;
int n,m,a[N],b[N],f[N][N];
signed main(){
//freopen();
//freopen();
IOS
//int T;
cin>>n>>m;
F(1,i,1,n) cin>>a[i];
F(1,i,1,m) cin>>b[i];
memset(f,0x3f,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;++i) f[i][0]=i;
for(int i=1;i<=m;++i) f[0][i]=i;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
f[i][j]=Min(Min(f[i-1][j]+1,i+j),Min(f[i][j-1]+1,f[i-1][j-1]+(a[i]!=b[j])));
}
cout<<f[n][m]<<'\n';
return 0;
}
NO.7 ARC102D
这题做了一年...之前就做了,结果WA了,然后又翻不了墙,看不了数据,也不知道错哪里。看题解又看不懂 \(3logn\),所以就放弃了。结果今天看了下发现,怎么会呢??
直接二进制就好了...
int n;
vector<tuple<int,int,int>> ans;
signed main(){
IOS
int l;
cin>>l;
int sum=1; n=1;
for(int i=1;(1<<i)<=l;++i){
sum<<=1,++n;
ans.pb(make_tuple(i,i+1,1<<(i-1)));
ans.pb(make_tuple(i,i+1,0));
}
int k=sum;
for(int i=n;i;--i){
if(l-sum>=k){
ans.pb(make_tuple(i,n,sum));
sum+=k;
} k>>=1;
}
if(sum!=l) ans.pb(make_tuple(1,n,sum++));
cout<<n<<' '<<ans.size()<<'\n';
for(auto x:ans)
cout<<get<0>(x)<<' '<<get<1>(x)<<' '<<get<2>(x)<<'\n';
return 0;
}
NO.8 AGC016B
这题感觉真的很经典耶...看着好简单,但是却很搞心态啊...还是太逊了...
首先最后没有要求输出方案,所以不妨先排个序,从大到小(无所谓的)。
-
思考
(手推)一下,如果某个颜色有重复,则这个位置的 \(ans\) 就是总颜色种类数;如果是单一颜色呢?那么就为 \(ans-1\)。所以最大值和最小值应该差不超过1。 -
如果最大和最小相等,则有两种情况。一是所有的颜色互不相同,则每个 \(ans\) 应该都等于 \(n-1\);二是每个颜色都有重复,那么总颜色数为 \(ans\),判断一下每个颜色是否能重复即可。
-
如果最大和最小不同,那么最大一定对应的是有重复的颜色,最小一定对应单一颜色。所以单一颜色数可以得到,总颜色数为 \(ans_1\),看看是否匹配即可。
const int N=100010;
int n,a[N];
signed main(){
IOS
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i];
sort(a+1,a+1+n,greater<int>());
int ans=1;
if(a[1]-a[n]>1) ans=-1;
else if(a[1]!=a[n]){//有重复颜色又有单一颜色
//因为相同的看到的一定是最多的
int t1=1,t2;
for(int i=2;a[i]==a[i-1];++i)
++t1;
t2=n-t1;//后t2种颜色一定不同,a[n]=a[1]-1
int a1=a[1];//由a[1]推出的总颜色数
int res=a1-t2;//有重复颜色的种类数
if(res*2>t1||t1<2||a1<t2+1) ans=-1;//不存在此方案
} else if(a[1]!=n-1){//要么都不同,要么都重复
//考虑都重复,a[1]为总颜色种类数
int dif=n-a[1];
if(dif<a[1]) ans=-1;
}
cout<<(ans==-1?"No":"Yes")<<'\n';
return 0;
}/*5 1 2 1 2 1*/