首页 > 其他分享 >The 13th Shandong ICPC Provincial Collegiate Programming Contest

The 13th Shandong ICPC Provincial Collegiate Programming Contest

时间:2023-12-03 16:55:05浏览次数:41  
标签:Provincial Shandong return Contest int CI cin vec include

Preface

昨天VP的那场没有字符串还没有原形毕露,今天遇到一个后期字符串直接和祁神大眼瞪小眼了

最后一个半小时祁神狂冲F成功写出两个version的假算法,而我已经滚去补之前欠下的题目了

赛后被徐神狠狠拷打了,评价为徐神是我们的红太阳,没有他我们都不能活


A. Orders

签到,按截止时间处理任务即可

#include<bits/stdc++.h>
using namespace std;
#define int long long

int t, n, k;

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> t;
	while (t--){
		cin >> n >> k;
		map<int, int> mp;
		for (int i=1; i<=n; ++i){
			int a, b; cin >> a >> b;
//			vec.push_back(Node{a, b});
			mp[a] += b;
		}
		bool ok=true;
		int cur=0;
		for (auto [a, b] : mp){
			cur += b;
			if (cur > a*k){ok=false; break;}	
		}
		cout << ( ok  ? "Yes\n" : "No\n");
	}
	return 0;	
}

B. Building Company

我看完题目人还蒙蔽着,祁神下来看了一眼就会了

考虑任务做了一定不亏,所以就想办法快速维护所有未处理的任务

给每个任务统计一下还有几个维度不满足,用堆来动态更新即可

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5+5;
int cnt[N];
struct mes{
	int u, id;
	bool operator<(const mes &b)const{return u>b.u;}	
};
struct Node{
	int cur=0;
	priority_queue<mes> Q;
};
map<int, Node> mp;
vector<pair<int, int> > vec[N];
int que[N], fr=0, ed=-1;

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	int g; cin >> g;
	for (int i=1; i<=g; ++i){
		int t, u; cin>>t>>u;
		mp[t].cur=u;	
	}
	int n; cin >> n;
	for (int i=1; i<=n; ++i){
		int m; cin >> m;
		for (int j=1; j<=m; ++j){
			int a, b; cin >> a >> b;
			Node &nd=mp[a];
			if (nd.cur < b) nd.Q.push(mes{b, i}), ++cnt[i];
		}
		int k; cin >> k;
		for (int j=1; j<=k; ++j){
			int a, b; cin >> a >> b;
			vec[i].emplace_back(a, b);
		}
	}
	
	for (int i=1; i<=n; ++i) if (0==cnt[i]) que[++ed]=i;	
	while (fr<=ed){
		int x=que[fr++];
		for (auto [t, u] : vec[x]){
			auto &nd = mp[t];
			nd.cur+=u;
			while (!nd.Q.empty() && nd.cur>=nd.Q.top().u){
				--cnt[nd.Q.top().id];
				if (0==cnt[nd.Q.top().id]) que[++ed]=nd.Q.top().id;
				nd.Q.pop();
			}
		}
	}
	
	int ans=0;
	for (int i=1; i<=n; ++i) if (0==cnt[i]) ++ans;
	//for (int i=1; i<=n; ++i) printf("%d ", cnt[i]); puts("");
	cout << ans << '\n';
	return 0;	
}

C. Trie

徐神远程讲出了这题做法的关键,但我是笨比完全策不懂的说

坐等徐神下周补题.png


D. Fast and Fat

思路很好想的一个题,首先最小值最大一眼二分答案,考虑如何检验最小速度为\(v_m\)是否合法

首先可以把人分成两类,一类是\(v_i<v_m\)的,这些人最后一定需要别人来背,可以先都扔进一个multiset

而另一类人是\(v_j\ge v_m\)的,考虑背人后仍要满足限制,则有\(v_j-(w_i-w_j)\ge v_m\),即\(w_i\le v_j+w_j-v_m\)

对于这类人我们贪心地在multiset里找一个体重满足限制并且最大的人背上即可,最后看看multiset是否为空

复杂度\(O(n\log^2 n)\)

#include<bits/stdc++.h>
using namespace std;

int t, n;
struct Node{
	int v, w;
	bool operator<(const Node &b)const{return v<b.v;}
};

vector<Node> vec;
bool check(int vm){
	multiset<int> st;
	for (auto [v, w] : vec){
		if (v<vm){ st.insert(w); continue;}
		auto it = st.upper_bound(w+v-vm);
		if (it==st.begin()) continue;
		--it;
		st.erase(it);
	}
	return st.empty();
}

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> t;
	while (t--){
		cin >> n;
		vec.resize(n);
		for (int i=0; i<n; ++i) cin >> vec[i].v >> vec[i].w;
		sort(vec.begin(), vec.end());
		
		int L=1, R=1e9+5;
		while (L<R){
			int M=L+(R-L)/2+1;
			if (check(M)) L=M;
			else R=M-1;	
		}
		cout << L << '\n';
	}
	return 0;	
}

E. Math Problem

因为特判写伪了两人人大眼看了好久

首先不难发现一定是先做完所有的除法再做乘法,否则如果存在相邻的两个操作一乘一除就相互抵消了,一定是劣的

首先特判掉\(k=1\)的情形,接下来我们发现除法的操作次数是\(\log n\)级别的,可以暴枚

同时考虑枚举接下来的乘法操作次数,这个的上界也是\(\log m\)级别的

判断的具体细则就是维护能得到的数的区间\([L,R]\),每次\(L'=L\times k,R'=R\times k +(k-1)\)

不难发现区间内的所有数都可以被表示出来,因此只要看\([L,R]\)中是否有\(m\)的倍数即可

单组数据复杂度\(O(\log n\times \log m)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int INF = 1e18+5;

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	int t, n, k, m, a, b;
	cin >> t;
	while (t--){
		cin >> n >> k >> m >> a >> b;
		if (1==k){
			if (m==1) cout << 0 << '\n';
			else cout << (n%m==0 ? 0 : -1) << '\n';
			continue;	
		}
		
		int ans=INF, res1=0, res2=0;
		while (n>=1){
			__int128 L=n, R=n;
			res2=0;
			while ((L-1)/m == R/m){
				res2+=a; L*=k; R=R*k+k-1;
			}
			ans = min(ans, res1+res2);
			res1+=b; n/=k;
		}
		ans = min(ans, res1);
		cout << ans << '\n';
	}
	return 0;	
}

F. Colorful Segments

这题祁神比赛时候提出的算法正好是出题人题解中讲的假算法,可以说是被完全预判到了

最后这题交给祁神补了,因此我就不写做法了,具体看官方题解



G. Matching

签到题,把所有\(a_i-i\)相同的数放在一起,贪心地每次取出最大的两个匹配即可

#include<bits/stdc++.h>
using namespace std;
#define int long long


signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	int t, n;
	cin >> t;
	while (t--){
		cin >> n;
		map<int, vector<int>> mp;
		for (int i=1; i<=n; ++i){
			int a; cin >> a;
			mp[a-i].push_back(a);
		}
		int ans=0;
		for (auto [v, vec] : mp){
			sort(vec.begin(), vec.end(), greater<int>());	
			for (int i=0; i+1<(int)vec.size(); i+=2){
				int res=vec[i]+vec[i+1];
				if (res<=0) break;
				ans+=res;
			}
		}
		cout << ans << '\n';
	}
	return 0;	
}

H. Be Careful 2

本场放AK题,然而我开场就开到这道题了,看来徐神不在我继承了徐神的被动可还行


I. Three Dice

签到,直接\(6^3\)爆枚即可

#include<bits/stdc++.h>
using namespace std;

bool vis[20][20];
signed main(){
	for (int i=1; i<=6; ++i){
		for (int j=1; j<=6; ++j){
			for (int k=1; k<=6; ++k){
				int A=0, B=0;
				if (i==1 || i==4) A+=i; else B+=i;
				if (j==1 || j==4) A+=j; else B+=j;
				if (k==1 || k==4) A+=k; else B+=k;
				vis[A][B]=true;
			}
		}
	}
	int a, b; cin >> a >> b;
	cout << (vis[a][b] ? "Yes\n" : "No\n");
	return 0;	
}

J. Not Another Path Query Problem

很经典的一个题,看一眼就会做了的程度

发现\(V\)是全局固定的,这就启发我们可以先找出一些前缀,满足当选择的边的权值有这个前缀时就一定满足大于等于\(V\)的条件

这个东西很经典,只要将\(V\)的每一个\(0\)的位置尝试替换成\(1\),然后保留高位的前缀,这样剩下的低位都可以任意取

最后可以发现只需要处理\(O(\log V)\)级别的前缀,那么我们给每个前缀建一张图然后用并查集维护下连通性即可

总复杂度\(O(m\log V\times \alpha(m))\)

#include<cstdio>
#include<iostream>
#include<vector>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
const int N=500005;
int n,m,q,V,x[N],y[N],w[N];
struct DSU
{
	int fa[N];
	inline void init(CI n)
	{
		for (RI i=1;i<=n;++i) fa[i]=i;
	}
	inline int getfa(CI x)
	{
		return fa[x]!=x?fa[x]=getfa(fa[x]):x;
	}
	inline void merge(CI x,CI y)
	{
		fa[getfa(x)]=getfa(y);
	}
	inline bool query(CI x,CI y)
	{
		return getfa(x)==getfa(y);
	}
}D[65];
signed main()
{
	//freopen("J.in","r",stdin); freopen("J.out","w",stdout);
	ios::sync_with_stdio(0); cin.tie(0);
	RI i,j; for (cin>>n>>m>>q>>V,i=1;i<=m;++i) cin>>x[i]>>y[i]>>w[i];
	int W=0; vector <int> pre;
	for (i=60;i>=0;--i)
	{
		if ((V>>i)&1LL) { W|=(1LL<<i); continue; }
		pre.push_back(W|(1LL<<i));
	}
	pre.push_back(W); for (i=0;i<pre.size();++i)
	{
		D[i].init(n); for (j=1;j<=m;++j)
		if ((w[j]&pre[i])==pre[i]) D[i].merge(x[j],y[j]);
	}
	//for (auto x:pre) cout<<x<<endl;
	for (i=1;i<=q;++i)
	{
		int u,v; cin>>u>>v; bool flag=0;
		for (j=0;j<pre.size()&&!flag;++j)
		if (D[j].query(u,v)) flag=1;
		cout<<(flag?"Yes":"No")<<'\n';
	}
	return 0;
}

K. Difficult Constructive Problem

连着两题遇到这种字典序问题了,但这个题显然比昨天那个要简单好多

首先注意到一个关键性质,当这个字符串的开头和结尾的字符确定后,最后不管怎么填,相邻的不同pair的奇偶性不会改变

因此可以考虑对后缀做一个DP,令\(f_{i,0/1}=(L,R)\),表示对于从\(i\)开始的后缀,当\(s_i=0/1\)时,后面能凑出的相邻的不同pair的最小值和最大值

不难发现对于某个区间\([L,R]\),一定有\(L,R\)的奇偶性相同,并且若\(x\in[L,R]\)且\(x\)和\(L,R\)的奇偶性相同,则\(x\)也可以凑出来

有了这个我们就可以很容易地贪心构造答案了,从前往后每一位优先放\(0\),用上面的DP数组来保证始终能构造出解即可

具体实现的时候细节比较多,需要注意下,同时对于开头和结尾字符没有确定的情况我们直接暴枚一下即可

总复杂度\(O(n)\),还有注意特判\(n=1\)的情况

#include<cstdio>
#include<iostream>
#include<string>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,INF=1e9;
struct ifo
{
	int l,r;
	inline ifo(CI L=INF,CI R=-INF)
	{
		l=L; r=R;
	}
}f[N][2]; int t,n,k; char s[N]; string ans;
inline void solve(void)
{
	RI i; f[n][s[n]-'0']=ifo(0,0); f[n][(s[n]-'0')^1]=ifo();
	for (i=n-1;i>=1;--i)
	{
		f[i][0]=f[i][1]=ifo();
		auto upt=[&](CI x,CI y)
		{
			f[x][y]=ifo(min(f[x+1][y].l,f[x+1][y^1].l+1),max(f[x+1][y].r,f[x+1][y^1].r+1));
		};
		if (s[i]=='?') upt(i,0),upt(i,1); else upt(i,s[i]-'0');
	}
	if (f[1][s[1]-'0'].l%2!=k%2) return;
	if (k<f[1][s[1]-'0'].l||k>f[1][s[1]-'0'].r) return;
	string ret=""; ret+=s[1]; int cur=0;
	for (i=2;i<n;++i)
	{
		if (s[i]!='?')
		{
			cur+=(ret.back()!=s[i]); ret+=s[i]; continue;
		}
		int tmp=k-(cur+(ret.back()!='0'));
		if (tmp%2==f[i+1][0].l%2&&f[i+1][0].l<=tmp&&tmp<=f[i+1][0].r)
		{
			cur+=(ret.back()!='0'); ret+='0'; continue;
		}
		if (--tmp,tmp%2==f[i+1][1].l%2&&f[i+1][1].l<=tmp&&tmp<=f[i+1][1].r)
		{
			cur+=(ret.back()!='0'); ret+='0'; continue;
		}
		tmp=k-(cur+(ret.back()!='1'));
		if (tmp%2==f[i+1][1].l%2&&f[i+1][1].l<=tmp&&tmp<=f[i+1][1].r)
		{
			cur+=(ret.back()!='1'); ret+='1'; continue;
		}
		if (--tmp,tmp%2==f[i+1][0].l%2&&f[i+1][0].l<=tmp&&tmp<=f[i+1][0].r)
		{
			cur+=(ret.back()!='1'); ret+='1'; continue;
		}
		return;
	}
	ret+=s[n]; ans=min(ans,ret);
}
int main()
{
	//freopen("K.in","r",stdin); freopen("K.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		scanf("%d%d%s",&n,&k,s+1); ans="2";
		if (n==1)
		{
			if (k) puts("Impossible"); else
			{
				if (s[1]=='?') s[1]='0';
				putchar(s[1]); putchar('\n');
			}
			continue;
		}
		if (s[1]=='?'&&s[n]=='?')
		{
			s[1]='0'; s[n]='0'; solve();
			s[1]='0'; s[n]='1'; solve();
			s[1]='1'; s[n]='0'; solve();
			s[1]='1'; s[n]='1'; solve();
		} else if (s[1]=='?')
		{
			s[1]='0'; solve(); s[1]='1'; solve();
		} else if (s[n]=='?')
		{
			s[n]='0'; solve(); s[n]='1'; solve();
		} else solve();
		if (ans=="2") puts("Impossible"); else
		{
			for (RI i=0;i<ans.size();++i) putchar(ans[i]); putchar('\n');
		}
	}
	return 0;
}

L. Puzzle: Sashigane

小清新构造题

考虑我们初始时有一个\(1\times 1\)的正方形,我们考虑每一步把它的边长扩大\(1\),这样只要贴着原来的正方形放一个两边等长的L形即可

不难发现每次至少存在一个方向扩展后是合法的,因此没有无解的情况

写的时候需要搞清楚题目中输出的规则,因此这题就没有讲给祁神写而是自己爬上去搓了下

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=1005;
int n,xL,xR,yL,yR,vis[N][N];
inline bool check(CI r,CI c,CI h,CI w)
{
	if (r<1||r>n||c<1||c>n) return 0;
	if (h>0)
	{
		for (RI i=0;i<=h;++i) if (r+i<1||r+i>n||vis[r+i][c]) return 0;
	} else
	{
		for (RI i=h;i<=0;++i) if (r+i<1||r+i>n||vis[r+i][c]) return 0;
	}
	if (w>0)
	{
		for (RI i=0;i<=w;++i) if (c+i<1||c+i>n||vis[r][c+i]) return 0;
	} else
	{
		for (RI i=w;i<=0;++i) if (c+i<1||c+i>n||vis[r][c+i]) return 0;
	}
	return 1;
}
inline void paint(CI r,CI c,CI h,CI w)
{
	printf("%d %d %d %d\n",r,c,h,w);
	if (h>0)
	{
		for (RI i=0;i<=h;++i) vis[r+i][c]=1;
	} else
	{
		for (RI i=h;i<=0;++i) vis[r+i][c]=1;
	}
	if (w>0)
	{
		for (RI i=0;i<=w;++i) vis[r][c+i]=1;
	} else
	{
		for (RI i=w;i<=0;++i) vis[r][c+i]=1;
	}
}
int main()
{
	//freopen("L.in","r",stdin); freopen("L.out","w",stdout);
	scanf("%d%d%d",&n,&xL,&yL); xR=xL; yR=yL; vis[xL][yL]=1;
	puts("Yes"); printf("%d\n",n-1);
	for (RI len=1;len<n;++len)
	{
		if (check(xL-1,yL-1,len,len)) { paint(--xL,--yL,len,len); continue; }
		if (check(xR+1,yL-1,-len,len)) { paint(++xR,--yL,-len,len); continue; }
		if (check(xL-1,yR+1,len,-len)) { paint(--xL,++yR,len,-len); continue; }
		if (check(xR+1,yR+1,-len,-len)) { paint(++xR,++yR,-len,-len); continue; }
	}
	return 0;
}

M. Computational Geometry

好久没遇到计算几何了,但是无所谓祁神还是一样秒

这题其实很简单只要枚举固定的两个点的位置,中间部分的面积用前缀和算一下

而剩下一个点的最优选择很显然可以三分,然后这题就做完了

总复杂度\(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N = 1e5+5;
int t, n, k;
struct Pt{
	int x, y;
	int crs(Pt b){return x*b.y-y*b.x;}	
	Pt operator-(const Pt b)const{return Pt{x-b.x, y-b.y};}
}pt[N*2];
__int128 pre[N*2];

int dis(Pt p, Pt a, Pt b){
	return abs((p-a).crs(b-a));
}

int bina(int l, int r){
	int L=l+1, R=r-1;
	while (L<R){
		int M1 = L+(R-L)/2;
		int M2 = M1 + 1;
//		printf("M1=%d M2=%d\n", M1, M2);
		if (dis(pt[M1], pt[l], pt[r])>dis(pt[M2], pt[l], pt[r])) R=M2-1;
		else L=M1+1;
	}
	return L;
}	

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> t;
	while (t--){
		cin >> n >> k;
		for (int i=0; i<n; ++i){
			cin >> pt[i].x >> pt[i].y;
			pt[n+i]=pt[i];
		}
		pt[n*2]=pt[0];
		for (int i=1; i<=n; ++i) pre[i]=pre[n+i]=pt[i-1].crs(pt[i]);
		for (int i=1; i<=n*2; ++i) pre[i]+=pre[i-1];
		__int128 ans=0;
		for (int i=0; i<n; ++i){
			__int128 sum1=pre[i+k]-pre[i] + pt[i+k].crs(pt[i]);
			int id=bina(i+k, i+n);
			sum1 += (pt[i+n]-pt[id]).crs(pt[i+k]-pt[id]);
//			printf("i=%d sum=%d\n", i, sum1);
			ans=max(ans, sum1);
		}
		if (ans%2==1) cout << (int)(ans/2) << ".5\n";
		else cout << (int)(ans/2) << '\n';
	}
	return 0;	
}

Postscript

希望徐神的CPU能早日搓好,赶紧回来带飞我们吧

标签:Provincial,Shandong,return,Contest,int,CI,cin,vec,include
From: https://www.cnblogs.com/cjjsb/p/17873383.html

相关文章

  • AtCoder Beginner Contest 295
    B-Bombs题意:就是说有一种炸弹,能炸曼哈顿距离的障碍物,要你打印出炸完后的图模拟#include<bits/stdc++.h>usingnamespacestd;charmp[50][50];voidsolve(){ intn,m; cin>>n>>m; for(inti=1;i<=n;i++){ for(intj=1;j<=m;j++){ cin>>mp[i][j]; } } for......
  • AtCoder Beginner Contest 326
    B-326-likeNumbers题意:找到一个不小于n的数是326数,定义是思路:简单的模拟循环即可#include<bits/stdc++.h>usingnamespacestd;boolcheck(intx){ vector<int>a; while(x){ a.push_back(x%10); x/=10; } if(a[1]*a[2]==a[0])returntrue; elsereturnfalse;}......
  • The 2023 ICPC Asia Hefei Regional Contest Test D. Balanced Array
    Preface这题赛场上出了个关键点基本都想到的做法,但因为一个地方卡住了没过去导致不得不选择弃掉这道题赛后补了下发现\(O(n\logn)\)的做法是只差临门一脚了,但\(O(n)\)的做法还是trick性挺强的Solution首先考虑枚举\(k\),不难发现此时合法的前缀一定是个连续的区间,其中区间的......
  • The 2023 ICPC Asia Hefei Regional Contest Test I. Linguistics Puzzle
    Preface这题yysy真不难,但比赛的时候想出做法后没时间写了,只能遗憾地看着倒计时结束Solution直接上爆搜复杂度肯定会爆,考虑有哪些数是可以不用搜直接推出来的首先样例启发我们\(0,1\)这两个数很好确定,因为\(0\)对应的字母单独出现的次数肯定最多,而\(1\)作为两位的开头出现的次......
  • ALGO ARTIS Programming Contest 2023 Autumn(AtCoder Regular Contest 168)
    Preface先补一下这场ARC的博客,因为在来回合肥的路上一直在想这场的CD,所以有空后就先把这场补了A-<Inversion>不难发现对于一段连续的<,设其长度为\(x\),则它最少要贡献\(\frac{x(x+1)}{2}\)的答案而我们很容易构造一种方案刚好满足这个下界,只要让每段的结束比下一段的开头大......
  • The 2021 Sichuan Provincial Collegiate Programming Contest
    Preface下下周还要去打重庆市赛,最近就找些省赛来练练手不得不说省赛的签到题是真的多,人均10+的过题看起来还是很恐怖的这场虽然因为徐神缺席,而且前面的题目都让祁神来写导致罚时略高,但无所谓反正最后也摸到了11题(主要是没有字符串)A.Chuanpai某不知题意的签到#include<bits......
  • The 2023 ICPC Asia Hefei Regional Contest
    目录写在前面赛时FEJGC补题写在最后写在前面赛时题目按照过题顺序排序,赛后补题按照个人向难度排序。省流版:要寄了吗?没寄。赛时F开局我正开,过了五分钟发现已经有人手刹了F了,几分钟之内大屏幕上一车提交,看了一下发现是超级签到于是Nebulyu上机开写。冲完之后发现T了????\(10......
  • AtCoder Beginner Contest 330
    B-MinimizeAbs1思维题题意:给定一个范围,你选择一个数,使得思路:如果A[i]在l,r中间,那么直接打印就行,如果不是就打印就近的usingnamespacestd;voidsolve(){ intn,l,r; cin>>n>>l>>r; for(inti=1;i<=n;i++){ intx; cin>>x; if(x<l){ cout<<l<<"......
  • AtCoder Beginner Contest 326
    A-2UP3DOWN#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongvoidsolve(){inta,b;cin>>a>>b;if(a<bandb-a<=2)cout<<"Yes\n";elseif(a>banda......
  • AtCoder Beginner Contest 322
    A-FirstABC2#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#definempmake_pairusingvi=vector<int>;usingpii=pair<int,int>;voidsolve(){intn;strings;cin>>n>>s;......