[CCPC2021桂林]个人补题 AEGI
碎碎念
跟队友一起vp的一场,一个队友贡献了点思路就跑了,另一个队友想出了G的正解但是没有调出来,笨人做了A和E,比较菜,铁牌收场
B和D感觉可做,但没有找到能理解的题解(菜死了
补了G
A.A Hero Named Magnus
签到,输出\(2*n-1\)即可。
记得开long long
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
const int N=2e5+100;
int t,n,q,a[N];
void solve()
{
cin>>n;
cout<<n*2-1<<'\n';
}
main()
{
cin>>t;
while(t--) solve();
}
I. PTSD
队友签的,强强
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define int long long
using namespace std;
const int N = 1e6+10;
int t,n,m,x,k;
int a[N];
main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
for(cin >> t;t--;){
string s;
cin >> n >> s;
int c=0,ans=0;
for(int i=n;i;i--)
{
int p=i-1;
if(s[p]=='1'){
if(c) ans+=i,c--;
else c++;
}
else c++;
// cout << c <<" ";
}
cout << ans << "\n";
}
}
G. Occupy the Cities
笨人的做法假了,队友的思路是正解,然而双双\(WA2\)
对于每一个\(0\)(设其在第i位),记录其两端最近的\(1\)的位置\(lef[i],rig[i]\),二分答案\(mid\),对于一个第\(i\)位上的\(0\),如果其与左右端最近的\(1\)的距离\(<=mid-1\)(即左右端最近的\(1\)可以朝反方向扩展一次后再朝着这个\(0\)的方向扩展),则花费\(mid\)代价时此点的\(0\)一定会被标记,如果其与左右端最近的\(1\)的距离\(==mid\),则这个\(1\)的扩展方向已被确定,往后如果存在一个\(0\)的左右端最近\(1\)为这个\(1\),且距离也为\(mid\),则不能再用这个\(1\)。从左向右扫一遍判断即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
#define inf 0x3f3f3f3f
const int N=1e6+100;
int t,n,q,lef[N],rig[N];
char s[N];
bool st[N];
bool check(int mid)
{
for(int i=1;i<=n;i++) st[i]=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='1') continue;
int minn=min(i-lef[i],rig[i]-i)+1;
if(minn<=mid) continue;
if(lef[i]>=1&&!st[lef[i]]&&i-lef[i]==mid){st[lef[i]]=1;continue;}
else if(rig[i]<=n&&!st[rig[i]]&&rig[i]-i==mid){st[rig[i]]=1;continue;}
else return 0;
}
return 1;
}
void solve()
{
cin>>n;
cin>>(s+1);
int x=-inf;
for(int i=1;i<=n;i++)
{
lef[i]=x;
if(s[i]=='1') x=i;
}
x=inf;
for(int i=n;i>=1;i--)
{
rig[i]=x;
if(s[i]=='1') x=i;
}
int l=0,r=n;
while(l<r)
{
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<'\n';
}
main()
{
IOS
cin>>t;
while(t--) solve();
}
E.Buy and Delete
答案最多是2,如果有环,Bob必须删两次,第一次拆环使剩下所有边都不成环,第二次把剩余边删除。如果不成环,Bob删一次即可。如果买不起任何一条边,Bob删0次。
只有三种情况:
- Alice一条边都买不起,Bob删\(0\)次。
- 图中的最小环权值<=Alice拥有的钱数,Bob删\(2\)次。
- 否则,Bob删\(1\)次。
对于此题来说,\(n<=2000,m<=5000\),属于稀疏图,使用\(dijkstra\)判最小环(\(O(n*(n+m)*logn\)))优于\(Floyd\)(\(O(n^3)\))
学了一手有向图最小环判断
(然而我前两发脑抽了写了个\(m*m*logn\)的dij,成功T6)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
const int N=5e3+100;
int t,n,m,c;
int h[N],e[N],ne[N],w[N],d[N][N],idx;
bool st[N];
int u,v,o;
struct node{
int u,v,w;
}a[N];
priority_queue<pair<int,int> > q;
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void dijkstra(int pos)
{
memset(st,0,sizeof st);
d[pos][pos]=0;
q.push({0,pos});
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(st[x]) continue;
st[x]=1;
for(int i=h[x];~i;i=ne[i])
{
//cout<<x<<' ';
int y=e[i],z=w[i];
if((u==x&&v==y)) continue;
d[pos][y]=min(d[pos][y],d[pos][x]+z);
if(!st[y]) q.push({-d[pos][y],y});
}
}
}
void solve()
{
memset(h,-1,sizeof h);
memset(d,0x3f,sizeof d);
int n,m,c;cin>>n>>m>>c;
int mi=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
cin>>a[i].u>>a[i].v>>a[i].w;
add(a[i].u,a[i].v,a[i].w);
mi=min(mi,a[i].w);
}
if(mi>c)
{
cout<<0;return;
}
mi=0x3f3f3f3f;
for(int i=1;i<=n;i++) dijkstra(i);
for(int i=1;i<=m;i++)
{
u=a[i].u,v=a[i].v,o=a[i].w;
if(d[v][u]>=0x3f3f3f3f) continue;
int res=d[v][u]+o;
mi=min(mi,res);
if(mi<=c)
{
cout<<2;return;
}
}
cout<<1;return;
}
main()
{
IOS
solve();
}

浙公网安备 33010602011771号