[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次。

只有三种情况:

  1. Alice一条边都买不起,Bob删\(0\)次。
  2. 图中的最小环权值<=Alice拥有的钱数,Bob删\(2\)次。
  3. 否则,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();
}

posted @ 2022-08-27 18:38  Hssliu  阅读(39)  评论(0)    收藏  举报