我做 AGC 感觉主要是大考试之前紧急加训。所以导致半年了还没做过 20 套。感觉还有两个月大概是做不到 40 套了。算了摆大烂。
事实上我们可以得到结论:如果放歌没有词那大概不会放出来,但是如果有一点点词就能过。目前不知道词要占到绝对数值或者相对比例的多少。不过可以肯定的是绝对数值够了那没什么问题,只要看相对比例。下次放 Aegleseeker 试验。
蚌埠,今天已经 12 通过(还没写 F)但是 ABCD 有 10 个。
[AGC014A] Cookie Exchanges
依题意模拟。容易发现不超过 \(\log\) 次,因此次数大直接退出。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
long long a,b,c;
int main(){
scanf("%lld%lld%lld",&a,&b,&c);
int cnt=0;
while(1){
if((a&1)||(b&1)||(c&1)){
printf("%d\n",cnt);
return 0;
}
cnt++;
int tmp1=a>>1,tmp2=b>>1,tmp3=c>>1;
a=tmp2+tmp3,b=tmp1+tmp3,c=tmp2+tmp3;
if(cnt>100)break;
}
puts("-1");
return 0;
}
[AGC014B] Unplanned Queries
憎恨结论题。
假设有一个点出现了奇数次,那么把奇数次分配到所有出边上,一定有一条边只被覆盖奇数次,故不合法。因此合法必须是全偶数次。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
int n,m,a[100010];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
a[u]++;a[v]++;
}
for(int i=1;i<=n;i++)if(a[i]&1){
puts("NO");return 0;
}
puts("YES");
return 0;
}
[AGC014C] Closed Rooms
之前见过。没做。
先走再解锁不太好算,直接先走一步变成先解锁再走。这是容易算的,最短路直接是和某个边界的距离。于是把起点距离不超过 \(k\) 的所有点统计一遍答案就行了。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <iostream>
using namespace std;
const int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
int n,m,k,ans=0x3f3f3f3f,dis[810][810];
char s[810][810];
queue<pair<int,int> >q;
bool check(int x,int y){
return x>=1&&x<=n&&y>=1&&y<=m&&s[x][y]!='#';
}
int getans(int x,int y){
int ans=0x3f3f3f3f;
int d=x-1;
ans=min(ans,(int)ceil(1.0*d/k));
d=n-x;
ans=min(ans,(int)ceil(1.0*d/k));
d=y-1;
ans=min(ans,(int)ceil(1.0*d/k));
d=m-y;
ans=min(ans,(int)ceil(1.0*d/k));
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
dis[i][j]=-1;
if(s[i][j]=='S'){
q.push(make_pair(i,j));dis[i][j]=0;
}
}
while(!q.empty()){
pair<int,int>p=q.front();q.pop();
ans=min(ans,getans(p.first,p.second)+1);
if(dis[p.first][p.second]==k)continue;
for(int i=0;i<4;i++){
int x=p.first+dx[i],y=p.second+dy[i];
if(~dis[x][y])continue;
if(check(x,y)){
dis[x][y]=dis[p.first][p.second]+1;
q.push(make_pair(x,y));
}
}
}
printf("%d\n",ans);
return 0;
}
[AGC014D] Black and White Tree
你说的对,但是为啥这么多博弈论。
考虑这样一个过程:先手先选了叶子的一个父亲,那后手必须选叶子。这样就消掉了树上的两个点。最后后手必胜等价于能消完。显然这就相当于求树有没有完美匹配,贪心即可。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int n,ans;
struct node{
int v,next;
}edge[200010];
int t,head[100010],siz[100010];
void add(int u,int v){
edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs(int x,int f){
siz[x]=1;int cnt=0;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f)dfs(edge[i].v,x),cnt+=siz[edge[i].v];
}
if(cnt)ans++,siz[x]=0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(1,0);
if((n&1)||ans!=(n>>1))puts("First");
else puts("Second");
return 0;
}
[AGC014E] Blue and Red Tree
这是好题。
倒着考虑,最后一次操作肯定是红蓝树的重边,然后此时蓝树上这条边两边的两个连通块一定已经处理好了。考虑倒数第二次操作(假设只有这一条红蓝边重合),那么也一定是类似的合并两个连通块的过程。于是倒着推回去,每次找到红蓝重边并合并两个端点,如果最后能合成一个就说明可以。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
using namespace std;
int n,l,r,fa[100010];
int find(int x){
return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
set<int>s[100010];
map<pair<int,int>,int>mp;
pair<int,int>q[200010];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=2*n-2;i++){
int u,v;scanf("%d%d",&u,&v);
s[u].insert(v);s[v].insert(u);
mp[make_pair(u,v)]++;mp[make_pair(v,u)]++;
if(mp[make_pair(u,v)]==2)q[++r]=make_pair(u,v);
}
while(l<r){
l++;
int u=find(q[l].first),v=find(q[l].second);
s[u].erase(v);s[v].erase(u);
if(s[u].size()<s[v].size())swap(u,v);
for(set<int>::iterator it=s[v].begin();it!=s[v].end();++it){
s[u].insert(*it);s[*it].erase(v);s[*it].insert(u);
mp[make_pair(*it,v)]=mp[make_pair(v,*it)]=0;
mp[make_pair(*it,u)]++;mp[make_pair(u,*it)]++;
if(mp[make_pair(*it,u)]==2)q[++r]=make_pair(*it,u);
}
fa[v]=u;s[v].clear();
}
for(int i=1;i<=n;i++)if(find(i)!=find(1)){
puts("NO");return 0;
}
puts("YES");
return 0;
}
[AGC014F] Strange Sorting
神秘 4201。超级思维题。反思一下为啥切不动 F。AGC 好像就喜欢这种代码不到 1k 的金牌题。
场上唯一切的好像是个平衡树依题意模拟。
首先设 \(T_i\) 为只考虑值在 \([i,n]\) 内元素,需要多少步排好序。
假设 \(T_{i+1}=0\),那么如果 \(i\) 在开头则 \(T_i=0\),否则 \(T_i=1\)。否则,如果在序列开头那么 \(T_i=T_{i+1}\),否则 \(T_i=T_{i+1}+1\)。问题变成如何判断 \(i\) 是否在 \([i+1,n]\) 的所有元素前面。
考虑 \(T-1\) 步时 \([i+1,n]\) 开头是什么,显然不是 \(i+1\)。假设是 \(f_{i+1}\)。注意到如果 \(i,i+1,f_{i+1}\) 在此时满足顺序是 \(f_{i+1},i,i+1\) 则进行一步之后 \(i\) 变成开头。那么有一个结论是如果这三个数在初始序列中的位置和这个循环同构就可行。这个需要证明任意操作不会改变循环顺序。
先有一个引理:\(f\) 如果不在开头那么一定不能被挪到后边。证明:假设可以挪到后边,那么它将永远在第一个元素后边,不可能成为 \(f\) 到达开头。
然后考虑 \(i,i+1,f\) 的所有情况,分讨所有情况之后可以得到 \(1,2,f\) 的循环顺序不变。因此证明了以上结论。于是倒着推回来就做完了。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <iostream>
using namespace std;
int n,pos[200010],t[200010],f[200010];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);pos[x]=i;
}
int cnt=0;
for(int i=n-1;i>=1;i--){
if(!t[i+1]){
if(pos[i]>pos[i+1])t[i]=1,f[i]=i+1;
continue;
}
if(pos[i]<pos[i+1]&&pos[i+1]<pos[f[i+1]]||pos[i+1]<pos[f[i+1]]&&pos[f[i+1]]<pos[i]||pos[f[i+1]]<pos[i]&&pos[i]<pos[i+1])t[i]=t[i+1],f[i]=f[i+1];
else t[i]=t[i+1]+1,f[i]=i+1;
}
printf("%d\n",t[1]);
return 0;
}
标签:AGC014,cnt,fa,int,++,edge,include
From: https://www.cnblogs.com/gtm1514/p/17438791.html