4.06号开始的题解-4.20 不含4.19的cf

https://codeforces.com/contest/2084/problem/C

排除下不合理的情况 这些都是很容易想到的 就是交换麻烦了点
这里上芋老师的代码 有很多地方需要我学习下 写的就很精简
首先是这两个的不同
vector<array<int, 2>> a(n);

	for (int j = 0; j < 2; j++) {
		for (int i = 0; i < n; i++) {
			cin >> a[i][j];
			a[i][j]--;
		}
	}

vector<vector> a(n+1, vector(m+1));

然后介绍下他是如何交换的 写的很好
我们记录第一行的数字为要交换的下标 然后直接开始换就行 不需要去提前把位置规划好
碰到对称不等的

直接开换 我先找到我那个本来对称的下表 就是w[a[i][1]] 然后n-1-i和这个互换就行
然后维护好两排交换 首先是两排元素换下 然后再改下双方的w下表 颠倒下都行没影响的

	auto swp = [&](int x, int y) -> void {
		res.push_back({x, y});
		swap(a[x], a[y]);
		swap(w[a[x][0]], w[a[y][0]]);可以颠倒 没影响的 
	//swap(w[a[x][0]], w[a[y][0]]);
	//	swap(a[x], a[y]);
	};
	for (int i = 0; i < n / 2; i++) {
		int j = n - 1 - i;
		if (a[i][0] == a[i][1]) swp(i, n / 2);
		if (a[i][1] != a[j][0]) swp(j, w[a[i][1]]);
	}

https://codeforces.com/contest/2084/problem/D

从数组中,重复地删除给定长度子数组,求剩余元素的问题
是一个结论题

此题他需要分成两种情况 那就是能不能直接变成小于k 或者大于等于k的情况
小于k那么直接套结论 最终就只能剩下
下标为0-n%k-1 任何一段k的都会被删除完的
大于等于k的话 那很显然我们只有分成m+1 每一组尽量大点就行 显然最多来到n/m+1那么大 余数也是要放的 就第一个结论那想不到
第二个猜都猜到了

if (n - m * k < k) {
	int w = n % k - 1;
	vector<int>v;
	for(int i=0;i<=w;i++)v.push_back(i);
	int siz = v.size();
	for (int i = 0; i <= n-1; i++) {
		if(i%k<=w){
			a[i]=i%k;
		}
		else a[i]=1e9;
	}
	for(int i=0;i<=n-1;i++)
		cout<<a[i]<<" ";
	cout << endl;
} else {
	int temp = m + 1;
	int cnt = n / temp;
	int cntrem = n % temp;
//cntrem是肯定不能要的
	for (int i = 1; i <= m + 1; i++) {
		for (int j = 0; j <= cnt - 1; j++) {
			cout << j << " ";
		}
		if (cntrem) {
			cout << cnt << " ";
			cntrem--;
		}
	}
	cout << endl;
}

https://codeforces.com/contest/1789/problem/C

直接枚举每一个数出现的长度之和 对于未出现的的数组之中cnt*(m+1−cnt) 对于两两的也有一个贡献 C(n,2)

for(int i=1;i<=m;i++){
		cin>>x>>y;
		if(a[x]!=y){
			cnt[a[x]]+=i-lst[a[x]],lst[a[x]]=-1;
			lst[y]=i,a[x]=y;
		}
	}
	for(int i=1;i<=n+m;i++) if(lst[i]!=-1) cnt[i]+=m-lst[i]+1;
	int ans=0;
	for(int i=1;i<=n+m;i++){
		ans+=cnt[i]*(m+1-cnt[i]);
		ans+=cnt[i]*(cnt[i]-1)/2;
	}

https://codeforces.com/contest/2093/problem/E

非常板子的一题 我就不放代码了 我只想介绍下t的原因

std::function<bool(int mid)>check=[&](int mid)->bool{ 
		if(mid==0)return 1;//跑不了这种情况 特判样例 
		set<int>s;
		for(int i=0;i<=mid-1;i++)s.insert(i);
		set<int>temp;//s要凑满  修改成	vector<bool>ww(mid,false);
//		int num=a[1];
		int num=0;
		int cost=mid;
		int w=mid;	//int now=mid;
		for(int i=1;i<=n;i++)
		{
			if(a[i]<mid&&temp.count(a[i])==0)	//if(a[i]<mid&&!ww[a[i]])
			{
				temp.insert(a[i]);	//ww[a[i]]=1;		now--;
			}			
			if(temp.size()==s.size())          
			{
				num++;
				if(num>=k)return 1;
				temp.clear();	  // ww.assign(mid, false);now=mid;
			}		
		}		
		return 0;		
	};

https://codeforces.com/contest/1793/problem/C

可以直接写双指针 双指针以后里面最好写if 不要再写while 容易死循环 我没写出双指针 我四个栈做的 懒得放了

int l = 1, r = n, maxn = n, minn = 1;
while (l < r) {
		if (l > r) {
			cout << -1 << endl;return ;
		}
		if (a[l] == maxn) {
			l++, maxn--;
			continue;
		}
		if (a[l] == minn) {
			l++, minn++;
			continue;
		}
		if (a[r] == maxn) {
			r--, maxn--;
			continue;
		}
		if (a[r] == minn) {
			r--, minn++;
			continue;
		}	
		cout<<l<<" "<<r<<endl;
		return ;
	}

https://codeforces.com/contest/1902/problem/D

这个题很难 首先我们存下xy前缀和的 存一个坐标的记录
然后判断她能不能经过 有三种可能性
我们在1-l-1 l r r+1 n 就过了
l r的判断最难 不过我们可以想清楚怎么判断呢 假设我们走到l-1
此时是x y 那么得到l r这一段的偏移量delta 如果说我们经过了这一个点的 假设st fin
至少我们有st-x fin-y的偏移量对吧 那么也就说
我们那个delta-(st-xl-1)+xl-1 肯定是存在的
化简得到xl-1+ xr -st yl-1+yr-fin存在

//#include<bits/stdc++.h>
//using namespace std;
//#define int long long
//#define debug  cout<<endl<<"---------"<<endl;

int prex[range];
int prey[range];
void solve(int t) {
	int n;
	int q;
	string s;
	cin >> n >> q >> s;
	s = ' ' + s;
	map<int, vector<int>>ma;
	ma[encode(0,0)].push_back(0);
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'U') {
			prey[i] = prey[i - 1] + 1;
			prex[i] = prex[i - 1];
		}
		if (s[i] == 'D') {
			prey[i] = prey[i - 1] - 1;
			prex[i] = prex[i - 1];
		}
		if (s[i] == 'R') {
			prey[i] = prey[i - 1];
			prex[i] = prex[i - 1] + 1;
		}
		if (s[i] == 'L') {
			prey[i] = prey[i - 1];
			prex[i] = prex[i - 1] - 1;
		}
		ma[encode(prex[i], prey[i])] .push_back(i);
	}
	std::function<bool(int x, int l,int r)>check = [&](int x, int l,int r)->bool{   
		if(ma[x].size()==0)return 0;
		vector<int>&temp=ma[x];
		auto it=lower_bound(temp.begin(),temp.end(),l);//-begin可以得到下标
		if(it==temp.end())return 0;
		if(*it<=r){
			return 1;
		}//
		return 0;
	};
	while (q--) {
		int x, y, l, r;
		cin >> x >> y >> l >> r;
		int w = encode(x, y);
		if (check(w, 0, l-1)) {
			cout << "YES" << endl;
			continue;
		}
		if (check(w, r, n)) {
			cout << "YES" << endl;
			continue;
		}		
		int nx = prex[l - 1] + prex[r] - x;
		int ny = prey[l - 1] + prey[r] - y;
		w = encode(nx, ny);
		if (check(w, l, r)) {
			cout << "YES" << endl;
			continue;
		}
		cout << "NO" << endl;
	}

//#include<bits/stdc++.h>
//using namespace std;
//#define int long long
//#define debug  cout<<endl<<"---------"<<endl;

int prex[range];
int prey[range];
void solve(int t) {
	int n;
	int q;
	string s;
	cin >> n >> q >> s;
	s = ' ' + s;
	map<int, vector<int>>ma;
	ma[encode(0,0)].push_back(0);
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'U') {
			prey[i] = prey[i - 1] + 1;
			prex[i] = prex[i - 1];
		}
		if (s[i] == 'D') {
			prey[i] = prey[i - 1] - 1;
			prex[i] = prex[i - 1];
		}
		if (s[i] == 'R') {
			prey[i] = prey[i - 1];
			prex[i] = prex[i - 1] + 1;
		}
		if (s[i] == 'L') {
			prey[i] = prey[i - 1];
			prex[i] = prex[i - 1] - 1;
		}
		ma[encode(prex[i], prey[i])] .push_back(i);
	}
	std::function<bool(int x, int l,int r)>check = [&](int x, int l,int r)->bool{   
		if(ma[x].size()==0)return 0;
		vector<int>&temp=ma[x];
		auto it=lower_bound(temp.begin(),temp.end(),l);//-begin可以得到下标
		if(it==temp.end())return 0;
		if(*it<=r){
			return 1;
		}//
		return 0;
	};
	while (q--) {
		int x, y, l, r;
		cin >> x >> y >> l >> r;
		int w = encode(x, y);
		if (check(w, 0, l-1)) {
			cout << "YES" << endl;
			continue;
		}
		if (check(w, r, n)) {
			cout << "YES" << endl;
			continue;
		}		
		int nx = prex[l - 1] + prex[r] - x;
		int ny = prey[l - 1] + prey[r] - y;
		w = encode(nx, ny);
		if (check(w, l, r)) {
			cout << "YES" << endl;
			continue;
		}
		cout << "NO" << endl;
	}

https://codeforces.com/contest/2094/problem/F

赛时用的偏移写法没过 其实只要分析m%k的关系就行了

void go() {
    int n, m, k;
    cin >> n >> m >> k;
    vector a(n, vector<int>(m, 0));
    if (m % k) {
        int cur = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                a[i][j] = (cur++) % k + 1;
            }
        }
    } else {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (i % 2 == 0) {
                    a[i][j] = j % k + 1;
                } else {
                    a[i][j] = (j + 1) % k + 1;
                }
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
}

https://codeforces.com/contest/1901/problem/C

说一个思路吧 其实每次都弄maxn+1就好了 关注一个相对大小 每次/2 数组最大最小的人不会变的

	while(mini!=maxn){
		mini=(mini+maxn+1)/2;
		ans++;
		temp.push_back(maxn+1);
	}
	//赛时只想到了+r 却不知道r+1
	if(ans>n){
		cout<<ans<<endl;return ;
	}
	cout<<ans<<endl;
	for(auto i:temp){
		cout<<i<<" ";
	}
	cout<<endl;

https://codeforces.com/contest/1901/problem/D

注意审题 要求你选一个i 而不是随机的 所以你要求一个最坏情况下的i
然后我们要想到一个i左边与右边 拆点去思考 最差的情况就是一个点从头开始和从尾巴开始
我们需要记录一个suf pre存最大的值 注意pre suf 他们的保存 肯定与你想的不同 pre是相对后

	for(int i=n;i>=1;i--){
		suf[i]=max(suf[i+1],a[i]+i-1);
	}
	for(int i=1;i<=n;i++){
	pre[i]=max(pre[i-1],a[i]+n-i);		
	}
	int ans=1e10;
	for(int i=1;i<=n;i++)
	{
		int temp=a[i];
		temp=max(temp,max(suf[i+1],pre[i-1]));		
		ans=min(ans,temp);
	}
	cout<<ans<<endl;
	

https://codeforces.com/contest/1895/problem/C

多模拟就会发现 对于一个数比如他长度为5可以拆1 4 前配3 也可以3 1 +2的
然后我们记录一个长度和sum和 去找 可以配对的
cnt1-now+cnt1:

for(int i=1;i<=n;i++){
		cin>>a[i];
		int siz=a[i].size();
		int now=0;
		for(int j=0;j<siz;j++)
		{
			now+=a[i][j]-'0';
		}
		ma[{siz,now}]++;
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		int siz=a[i].size();
		int now=0;
		for(int j=0;j<siz;j++)
		{
			now+=a[i][j]-'0';
		}
		int w=0;
		int cnt1=0;	int cnt2=0;
		for(int j=1;j<=siz;j++){
			
			w++;//w 表示前半段
			cnt1+=a[i][j-1]-'0';
			cnt2+=a[i][siz-1-j+1]-'0';
			int rem=siz-w;
			ans+=ma[{w-rem,cnt1-now+cnt1}];
			if(j!=siz){
				ans+=ma[{w-rem,cnt2-now+cnt2}];
			}		
		}
	}
	cout<<ans<<endl;

https://codeforces.com/contest/1895/problem/D

attention题
b1^b2=a1
b2^b3=a2
...b1bi=a1--ai-1
很像高中数学题啊 说实话打算竞不就是高中做题吗
所以本质上此题就是维护一个异或和 01tire一下就行 此题选一个b1才是关键
01的话数组31就行 字符的话是最大字符长度n

#include<bits/stdc++.h>
#define int long long
#define debug cout<<endl<<"----------"<<endl;
const int range = 8e5+10;
using namespace std;
int n, k;
int a[range];
int ch[range][2];//
int idx;
int cnt[range];
void insert(int x) {
	int p = 0;
	int res = 0;
	for (int i = 30; i >= 0; i--) {
		int u = (x >> i) & 1;
		if (!ch[p][u]) {
			ch[p][u] = ++idx;
		}
		p = ch[p][u];
		cnt[p] += 1;
	}
	return ;
}
int query(int x) {
	int p = 0;
	int res = 0;
	for (int i = 30; i >= 0; i--) {
		int u = bool((x >> i) & 1);
		if (cnt[ch[p][!u]]) {
			res += 1 << i;
			p = ch[p][!u];
		} else p = ch[p][u];
	}
	return res;
}
int pre[range];
void solve(int t ) { //多测
	cin >> n ; 
	for(int i=1;i<=n-1;i++){
		cin>>a[i];
		pre[i]=pre[i-1]^a[i];
		insert(pre[i]);
	}
	int ans=0;
	for(int i=0;i<=n-1;i++)
	{
		if((query(i))<=n-1){
			ans=i;//我了个不打括号就完蛋了 
//			debug
//			cout<<i<<endl;
			int w=query(i)^i;
//			cout<<query(i)<<" "<<w<<endl;
			break;;
		}
	}
	cout<<ans<<" ";
	for(int i=1;i<=n-1;i++)
	{
		cout<<(ans^pre[i])<<" ";
	}
	cout<<endl;
}

https://codeforces.com/contest/1879/problem/C

这题不难 无脑去找就行 注意审题 最终答案应该是

我没思考到阶乘 (样例都没过一遍就码了)

https://codeforces.com/contest/1886/problem/B


蛮难的 就是需要分类思考下
第一种就是离得很近 另一个点就没用了 相当于 ap----b这种 pa或者oa就行
另一种就是被夹中间了 就是图二 可能是ab/2相当于相切 当然不要忘记算oa ob

double  cal(int x,int y,int xx,int yy ){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy))*1.0;
}
void init(){
}
void solve(int t)
{
	int px,py;
	int ax,ay;
	int bx,by;
	cin>>px>>py>>ax>>ay>>bx>>by;
	//first
	double  ans=1e9;
	ans=min(max(cal(0,0,ax,ay),cal(ax,ay,px,py)),ans);
	ans=min(max(cal(0,0,bx,by),cal(bx,by,px,py)),ans);
	double x=cal(0,0,ax,ay);	double xx=cal(bx,by,px,py);
	double xxx=cal(ax,ay,bx,by);
	ans=min(ans,max(cal(0,0,ax,ay),max(cal(bx,by,px,py),cal(ax,ay,bx,by)*1.0/2.0)));
	ans=min(ans,max(cal(0,0,bx,by),max(cal(ax,ay,px,py),cal(ax,ay,bx,by)*1.0/2.0)));	
	cout<<setprecision(9)<<fixed<<ans<<endl;
}

https://codeforces.com/contest/1861/problem/C

很有意思的一道题
显然我们可以让最后一个造成降序排列 这是肯定的 我也观察到了 (但是wa了)
然后考虑操作
+肯定无脑加 -就有说法了 如果减的是造成降序排列 要标记此时无降 否则没啥
1的话就看降序否 0同理 有些地方坑 比如<=1的数组是可以1的 注意10后及时更新

void solve(int t)
{
	//题解思路真不错 
	string s;
	cin>>s;	
	int unfcnt=0;
	int fcnt=0;
	int cnt=0;
	bool flag=0;
	for(int i=0;i<=s.size()-1;i++)
	{
		if(s[i]=='+')cnt++;
		else if(s[i]=='1')
		{
			if(unfcnt==0){
				fcnt=cnt;continue;
			}
			else flag=1;
		}
		else if(s[i]=='0')
		{
			if(cnt<2||fcnt==cnt){
				flag=1;break;
			}
			if(unfcnt==0)unfcnt=cnt;	
		}
		else if(s[i]=='-')
		{
			cnt--;
			if(unfcnt>cnt)unfcnt=0;
			else if(fcnt>cnt)fcnt=cnt;			
		}		
	}
	if(flag){
		cout<<"NO"<<endl;return ;
	}
	else {
		cout<<"YES"<<endl;return ;
	}

https://codeforces.com/contest/1860/problem/C


读懂题目很重要 翻一下 注意是bob先走 alice只是先选一个数字 然后选择是任意的一个前面的下标
然后这个数字不能选不能走的 然后问你最终谁不能走 不能走 就他赢的
其实一个很显然的结论就是 必胜态和必败态的转化
这里不讨论题解的树状数组优化上升子序列的写法


我们可以时刻记录一个最小值 只要可以跳这个最小值的时候 都是必胜态 然后1010101这样分布的

	for(int i=1;i<=n;i++)
	{ 
		int win=0;
		if(a[i]<now){
			win=1;
			now=a[i];
		 continue;
		}
		else {
			if(a[i]>mini){
				win=1;
			}
			else {
				win=0;
				mini=a[i];
			}
		}
		if(win==0){
			ans++;
		}	
	}
	cout<<ans<<endl;

https://codeforces.com/contest/1849/problem/A

我是for循环的 没想出来o1操作是这样的

cout<<min(n-2,m+k-1)*2+3<<endl;

https://codeforces.com/contest/1849/problem/C

一眼考虑缩小区间 那么如何缩小呢
很明显 0011 1 4->2 3
排序只有是10这种才会被排序 我们可以记录l的右边第一个1 和r左边第一个0 那么这种区间段就是要被排序的


	//扇了自己10个巴掌 这没做出来
	cin>>n>>m;
	cin>>s;
	s=' '+s;
	pre[1]=1;
	for(int i=0;i<=n+10;i++){
		pre[i]=suf[i]=0;
	}
	//1->0
	for(int i=2;i<=n;i++)
	{
		if(s[i]=='0')pre[i]=i;
		else if(s[i]=='1'){
			pre[i]=pre[i-1];
		}
	}
	suf[n]=n;
	//0->1
	for(int i=n-1;i>=1;i--){
		if(s[i]=='0'){
			suf[i]=suf[i+1];
		}
		else {
			suf[i]=i;
		}	
	}
	int ans=0;
	//
	map<pair<int,int>,bool>ma;
	for(int i=1;i<=m;i++)
	{
		int l,r;
		cin>>l>>r;
		
		l=suf[l];
		r=pre[r];
//		cout<<l<<" "<<r<<" "<<ans<<endl;
		if(l>=r){
			l=r=0;
		}
		if(ma[{l,r}])continue;
		ma[{l,r}]=1;
		ans++;		
		
	}
	cout<<ans<<endl;

https://codeforces.com/contest/1845/problem/C

题目有点读不懂 注意看其实是l-r选一个而不是 s序列里选一个 同时lr是m长 而不是一长一短
所以我们可以直接分析这个字符串 每次贪心的挑最远的相同字母(对于l-r之间的那个值)这样就可以一直跳着跳着了

//11:
//把一个数由右边向左边数,将奇位上的数字与偶位上的数字分别加起来,
//再求它们的差,
//如果这个差是11的倍数(包括0),那么,原来这个数就一定能被11整除。
//5:
//个位上是0或5的数都是5的倍数
//9
//一个数是9的倍数,当且仅当它的各个数字之和是9的倍数
string l;
string r;
string s;
set<int>st[50];
void solve(int t ) { //多测
	//本题考察阅读理解 做了这么多题算难读懂的了 
	cin >> s;
	cin >> m;
	cin >> l >> r;
	for(int i=0;i<=10;i++)st[i].clear();
	for (int i = 0; i <= s.size() - 1; i++) {
		int w = s[i] - '0';
		st[w].insert(i);
	}
	int now = -1;
	for (int i = 0; i < m; i++) {
		
		int ww=0;
//		cout<<endl;
//		cout<<i<<endl;
		for (int j = l[i] - '0'; j <= r[i] - '0'; j++) {
			auto temp = st[j].upper_bound(now);
//			cout<<*temp<<" ";
			if (temp == st[j].end()) {
				cout << "YES" << endl;
				return ;
			}
			ww= max(ww, *temp);
		}
		now=max(now,ww);
	}
	cout<<"NO"<<endl;

https://codeforces.com/contest/1841/problem/C

这题实际上不是贪心的话很难!如果是贪心的话 其实证明是很复杂的 我是不会做的

只能提供题解思路了 我们对于一个数字是否修改有两只操作 一个是大一个是小

考虑我们修改是怎么找的

这里直接说了 对同一个数字而言 只管他的一左一右 你想想我改了中间的数 要是我右边还有数 那改小了没意义还变小了改大了右边改大了 没意义呀 因为你大了可能还造成之前的变负 除非你变E 你增的更多

左边是改大 而且是最左边 这样就好多了

int cal()
{
	int val=0;
	int maxn=0;
	for(int i=n-1;i>=0;i--)
	{
		if((s[i]-'A'+1)>=maxn){
			maxn=s[i]-'A'+1;
			val+=qpow(10,s[i]-'A');
//			cout<<val<<" "<<maxn<<endl;
		}
		else {
			val-=qpow(10,s[i]-'A');
//			cout<<val<<" "<<i<<endl;
		}
	}
	return val;	
}
void solve(int t)
{
	//憧憬成为算法高手的一天
	//nmmd 这题是真难 
	cin>>s;
	n=s.size();
	for(int i=1;i<=5;i++){
		last[i]=-1;
		st[i]=-1;
	}
	//这个写法很好 建议纳入板子
	for(int i=n-1;i>=0;i--)
	{
		if(last[s[i]-'A'+1]==-1){
			last[s[i]-'A'+1]=i;
		}
		st[s[i]-'A'+1]=i;
	}
	//gaid                 gai x
	int ans=0;
	ans=cal();
	for(int i=1;i<=5;i++)
	{		
		int now=st[i];
		if(now!=-1)
		{
			for(int j=i+1;j<=5;j++)
			{
//				cout<<j<<endl;
//				int now=st[i];
				char ch='A'+j-1;
				s[now]=ch;
				ans=max(ans,cal());
				s[now]='A'+i-1;
			}
		}
		now=last[i];
//		cout<<now<<endl;
		if(now!=-1)
		{
			for(int j=1;j<=i-1;j++)
			{
//			int now=st[i];
				char ch='A'+j-1;
				s[now]=ch;
				ans=max(ans,cal());
				s[now]='A'+i-1;
			}	
		}
		
	}
	cout<<ans<<endl;

https://codeforces.com/contest/1954/problem/C

这题还是说下吧 虽然直接切了 不过我的是贪心 没证明的 虽然这个证明自己是知道的

https://codeforces.com/contest/1954/problem/D

dp 此题的trick 就是关于这种集合类型的 比如说
1 2 3 分 1 2 13 2 3 的这种算贡献的 可以排序
然后按照数量排序 那么子集的最后一个数绝对是数量最大的 那么 计算贡献可以围绕这个数来展开 称为绝对众数
这题题目其实是很绕的 来解释下题目意思吧
其实 就是说给你2^n个集合 比如 1 2 3 问你把集合内的颜色的都放进去要多少个袋子 每一个袋子只能装两个球 比如说 我现在
3
1 1 2
放1 3 要多少个袋子 答案是2 因为 1 3 然后再3单独放
此题又为什么是dp呢 因为他其实是一个01背包 因为可以放可以不放 才有2^n种集合嘛
然后我们知道贡献怎么算 比如说 你此时是j个球 你绝对众数如果大于他 那很明显需要ai个袋子 否则是多少呢 其实 ai+j/2 (上) 多出来的

然后我们定义fj是放j个球的方案 然后 每次计算就好了

const int mod = 998244353;
int f[range];
void solve(int t) {
	cin >> n;
	int m=0;
	//真的是阅读理解题 读懂就已经很费事了 
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		m+=a[i];
	}
	sort(a+1,a+1+n);
	int ans = 0;
	f[0]=1;
	for (int i = 1; i <= n; i++) {		
		for(int j=0;j<=m-a[i];j++)
		{
			ans=(ans+(j>a[i]?(j+a[i]+1)/2:a[i])*f[j]%mod)%mod;	
		} 
		for(int j=m;j>=a[i];j--)
		{
			f[j]=f[j]+f[j-a[i]]%mod;
		}
	}
	cout<<ans<<endl;

posted @ 2025-04-20 18:17  LteShuai  阅读(18)  评论(0)    收藏  举报