AGC 049 部分简要题解

nmd 差1分钟过 E,真就老年选手不配进第一页/ll

A

计算每个点的贡献,做完了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 110;

string s[N];
int n;

bitset<N> tow[N];

int main()
{
	cin >> n;
	for(int i=0;i<n;i++)cin >> s[i],tow[i][i]=1;
	for(int i=0;i<n;i++)for(int j=0;j<n;j++)if(s[i][j]=='1')tow[j][i]=1;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++)for(int k=0;k<n;k++){
			if(tow[j][i] && tow[i][k])tow[j][k] = 1;
		}
	}
	typedef double db;
	db ans = 0;
	for(int i=0;i<n;i++){
		int p = tow[i].count();
		// cout << p << endl;
		ans += (db)1/(db)p;
	}
	// cout << ans << endl;
	printf("%.20lf",ans);
}

B

可以看作是移动所有 1。先贪心找 t 中每个 1 从哪里移动过来可以证明正确。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;

inline int readc(){
	char x=0;while(!isdigit(x))x=getchar();
	return x-'0';
}

int s[N], t[N], n;
int ss,tt;
int f=0;

int main()
{
	cin >> n;
	for(int i=1;i<=n;i++)s[i]=readc(),ss++;
	for(int i=1;i<=n;i++)t[i]=readc(),tt++;
	if(ss<tt||ss%2!=tt%2){puts("-1");exit(0);}
	long long ans=0;
	for(int i=1;i<=n;i++){
		if(t[i])++f,ans-=i;
		if(s[i] && f)--f,ans+=i,s[i]=0;
	}
	if(f){puts("-1");exit(0);}
	for(int i=1;i<=n;i++){
		if(s[i]){
			f^=1;
			if(f)ans-=i;
			else ans+=i;
		}
	}
	cout << ans << endl;
}

C

判断的话是对于每个位置计算 \(a_i - b_i\),如果 \(<0\) 一定有一个 \(aj - b_j\ge 0(j\ge i)\) 的小于等于 \(a_i\)(考虑一个位置如果一定要被删除那么其实可以不考虑这个位置删除了谁,因为一定可以看作另外一个删除了它。

你移动一个可以弄一个 \(a_{i}+1, 1\) 出来,把 \(a_i\) 安排掉,或者直接把一个 \(a_i-b_i\) 取到 \(\ge 0\),之后暴力就行。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
typedef pair<int,int> pii;
int n;
int a[N], b[N], lst[N];

vector<pii> Vec;
vector<int> vec2;

int main()
{
	cin >> n;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	int last = a[n]+1;
	vector<int> cost;
	for(int i=n;i;i--){
		lst[i] = a[i] - b[i];
		// cout << lst[i] << ":" << last << endl
		if(lst[i]>0)last = min(last, lst[i]);
		else if(last <= a[i]){
			vec2.push_back(a[i]),Vec.push_back(pii(last,1-lst[i])),cost.push_back(0);
		}
		else {
			// cout << i << ":" << lst[i] << "::" << last << endl;
			vec2.push_back(a[i]),Vec.push_back(pii(last,1-lst[i]));
			cost.push_back(1);
		}
	}
	reverse(Vec.begin(),Vec.end());
	reverse(vec2.begin(),vec2.end());
	reverse(cost.begin(),cost.end());
	if(Vec.empty()){
		puts("0");
	}else{
		int ans = 0x3f3f3f3f;
		for(int i=(int)Vec.size()-1;~i;i--){
			if(i+1<(int)Vec.size())cost[i]+=cost[i+1];
		}
		
		for(int i=-1;i<(int)Vec.size();++i){
			int w=0;
			if(i!=-1)w=Vec[i].second;
			if(i+1<(int)Vec.size()){
				w=max(w, cost[i+1]);
			}
			// cout << i << ":" << w << endl;
			ans = min(ans, w);
		}
		cout << ans << endl;
	}
}

D

相当于是要求它是下凸的。

考虑分成斜率 \(>0,=0,<0\) 三个部分。
对于 \(1,3\) 部分,我们考虑斜率的差分:\(f[i][j]\) 表示 \(i\) 个数,和是 \(j\) 的结果。如果这个位置的斜率是 \(t\)(这个时候后面全部要斜率 \(+t\)),会增加 \(t*i*(i-1)/2\),暴力dp即可(第一维只有根号)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
inline int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
inline int sub(int a,int b){a-=b;return a<0?a+mod:a;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
inline int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
/* math */
int n,m;
int dp[450][100010], f[450][100010], g[450][100010];

int main()
{
	cin >> n >> m;
	dp[1][0]=f[1][0]=g[1][0]=1;
	int mx=1;
	for(int i=1;i*(i-1)/2<=m;i++){
		mx=i;
		if(i>1){
			int A = i*(i-1)/2;//1+2+..+i-1
			for(int j=0;j<=m;j++){
				if(j>=A)dp[i][j] = add(dp[i-1][j-A], dp[i][j-A]);
				f[i][j] = dp[i][j];
			}
		}
		for(int j=0;j<=m;j++){
			dp[i][j]=add(dp[i][j],dp[i-1][j]);
			g[i][j]=dp[i][j];
			if(j>=n)g[i][j]=add(g[i][j],g[i][j-n]);
		}
	}
	int ans=0;
	// cout << mx << endl;
	for(int i=1;i<=min(n,mx);i++){
		int i2 = n+1-i;
		i2=min(i2,mx);
		for(int j=0;j<=m;j++){
			// cout << i << ":" << j << "::" << f[i][j] << ":" << g[i2][m-j] << endl;
			ans=add(ans, mul(f[i][j],g[i2][m-j]));
		}
	}
	cout << ans << endl;
}

E

考虑原问题可以转化成:

给一个二维的矩阵,其中 \(\forall_{1\le i\le f_k} G(k,i) = 1\)。每次可以单点异或 \(1\) 或者一行颜色相同的异或 \(1\),代价分别是 \(1\) 或者 \(C\)

于是原问题转化成了 \(O(nk)\)\(B_{i,j} = \{0,1\}\) 的问题。

考虑 \(B \in \{0,1\}\) 如何求解最大值的计数。

首先对于这种情况预先算出全部都是用区间操作的贡献。

现在相当于是要对若干位置改成单点操作,显然一段极长的区间要么全部操作,要么全部不操作,同样不可能同时操作两个相邻的区间(这样显然一边不操作不会变劣)。

那么现在实际上有一个 \(O(n)\) dp:记录所有不相等位置 \(p\)\(f[i] = \max(f[i-1], p_i - p_{i-1} - C + f[i-2])\)

考虑两种转移的差,不难发现每次遇到一个连续相等的差会 \(+1\),遇到不相等的则根据差的正负重新算差,并且这个差的绝对值 \(\le n\)

将两种转移的差记录下来作为状态,dp 值分别记录方案数以及 dp 值之和即可。

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
inline int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
inline int sub(int a,int b){a-=b;return a<0?a+mod:a;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
inline int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
/* math */
const int N = 55,S=N;
int n,c,k;
int b[N][N];
int w[2][N];
vector<int> v;

int dp[N][2][N*2],g[N][2][N*2];
inline int query(){
	memset(dp, 0, sizeof dp);
	memset(g, 0, sizeof g);
	int v=1;
	int all = 1;
	w[0][0]=1;
	w[0][n+1]=1;
	for(int i=0;i<=n;i++){
		all = mul(all, add(w[0][i], w[1][i]));
	}
	int S0 = 0;
	for(int i=1;i<=n;i++){
		int ww = mul(all, mul(qpow(add(w[0][i],w[1][i]),mod-2),qpow(add(w[0][i-1],w[1][i-1]),mod-2)));
		// cout << ww << endl;
		S0 = add(S0, mul(ww,mul(w[0][i-1],w[1][i])));
	}
	S0 = mul(S0, c);
	// cout << S0 << endl;
	for(int i=1;i<=n+1;v=mul(v,w[0][i++])){
		// cout << i << "::" << v << "," << w[0][i] << " " << w[1][i] << endl;
		dp[i][1][c+S]=v,g[i][1][c+n*n]=0;
		for(int j=S-n;j<=S+n;j++){
			int sum = j-S-1;
			for(int k=0;k<2;k++){
				dp[i][k][S+sum] = add(dp[i][k][S+sum],dp[i-1][k][j]);
				g[i][k][S+sum] = add(g[i][k][S+sum],g[i-1][k][j]);
				if(sum>0){
					dp[i][k^1][S-sum+c] = add(dp[i][k^1][S-sum+c],dp[i-1][k][j]);
					g[i][k^1][S-sum+c] = add(g[i][k^1][S-sum+c],g[i-1][k][j]);
					// cout << i << ":" << sum << endl;
					g[i][k^1][S-sum+c] = add(g[i][k^1][S-sum+c],mul(dp[i-1][k][j],sum));
				}else{
					dp[i][k^1][S+c] = add(dp[i][k^1][S+c],dp[i-1][k][j]);
					g[i][k^1][S+c] = add(g[i][k^1][S+c],g[i-1][k][j]);
					g[i][k^1][S+c] = add(g[i][k^1][S+c],mul(dp[i-1][k][j],0));
				}
			}
		}
		for(int j=S-n;j<=S+n;j++){
			for(int k=0;k<2;k++){
				dp[i][k][j] = mul(dp[i][k][j], w[k][i]);
				g[i][k][j] = mul(g[i][k][j], w[k][i]);
				// if(dp[i][k][j])cout << i << ":" << k << ":" << j-S << "::" << dp[i][k][j] << ":" << g[i][k][j] << endl;
			}
		}
	}
	int ans = 0;
	for(int i=n+1;i<=n+1;i++){
		for(int j=S-n;j<=S+n;j++){
			ans = add(ans, g[i][0][j]);
			ans = add(ans, g[i][1][j]);
		}
	}
	// cout << S0 << ":" << ans << endl;
	// cout << ans << endl;
	// system("pause");
	return sub(S0, ans);
}

int main()
{
	cin >> n >> c >> k;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=k;j++){
			cin >> b[i][j];
			v.push_back(b[i][j]);
		}
	}
	v.push_back(0);
	sort(v.begin(), v.end());
	int ans = 0;
	for(int t=0;t<n*k;t++)if(v[t]!=v[t+1]){
		// cout << v[t] << " to " << v[t+1] << endl;
		for(int i=1;i<=n;i++){
			w[0][i] = w[1][i] = 0;
			for(int j=1;j<=k;j++){
				if(b[i][j]>=v[t+1])w[1][i]++;
				if(b[i][j]<=v[t])w[0][i]++;
			}
			// cout << w[0][i] << ":" << w[1][i] << endl;
		}
		int W = query();
		ans = add(ans, mul(W, v[t+1]-v[t]));
	}
	cout << ans << endl;
}
posted @ 2020-11-16 23:50  jerome_wei  阅读(572)  评论(2编辑  收藏  举报