CF1408

那个博客搭好遥遥无期。

A:

直接做就行了,我没智力还写 \(dp\)

#include<bits/stdc++.h>
using namespace std;
#define MAXN 110
int n;
int p[MAXN][3];
bool f[MAXN][3];
int pre[MAXN][3];
int ans[MAXN];
void work()
{
	scanf("%d",&n);
	for(int j = 0;j < 3;++j)for(int i = 1;i <= n;++i)scanf("%d",&p[i][j]);
	for(int v = 0;v < 3;++v)
	{
		memset(f,0,sizeof(f));
		f[1][v] = true;
		for(int i = 2;i <= n;++i)
		{
			for(int y = 0;y < 3;++y)
			{
				for(int x = 0;x < 3;++x)
				{
					if(p[i - 1][x] != p[i][y] && f[i - 1][x])
					{
						f[i][y] = true;
						pre[i][y] = x;
						break;
					}
				}
			}
		}
		bool tag = false;
		for(int i = 0;i < 3;++i)
		{
			if(f[n][i] && p[n][i] != p[1][v])
			{
				for(int k = n,cur = i;k >= 1;--k)
				{
					ans[k] = p[k][cur];
					cur = pre[k][cur];
				}
				for(int k = 1;k <= n;++k)printf("%d ",ans[k]);puts("");
				tag = true;
				break;
			}
		}
		if(tag)break;
	}
	return;
}
int main()
{
	int testcases;
	scanf("%d",&testcases);
	while(testcases--)work();
	return 0;
}

B:

还挺有意思的,就是从左往右数如果颜色达到\(k\)了就把剩下的拿出去再一样做,统计颜色段数算一算。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 110
int n,k;
int a[MAXN];
void work()
{
	scanf("%d%d",&n,&k);
	for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
	int cnt = 0;
	for(int i = 1;i < n;++i)if(a[i] != a[i + 1])++cnt;
	if(cnt == 0)puts("1");
	else if(k == 1)puts("-1");
	else
	{
		++cnt;
		int ans = 0;
		while(cnt > 0)
		{
			++ans;
			cnt -= k;
			if(cnt <= 0)break;
			++cnt;
		}
		cout << ans << endl;
	}
	
	return;
}
int main()
{
	int testcases;
	scanf("%d",&testcases);
	while(testcases--)work();
	return 0;
}

C:

算到每个位置两人的速度和时间,这样即可得知在哪段相遇,然后就能算了。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
int n,l;
int a[MAXN];
double ta[MAXN],tb[MAXN];
int va[MAXN],vb[MAXN];
void work()
{
	scanf("%d%d",&n,&l);
	for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
	a[n + 1] = l;
	ta[0] = 0;va[0] = 1;
	for(int i = 1;i <= n + 1;++i)
	{
		va[i] = va[i - 1] + 1;
		ta[i] = double(a[i] - a[i - 1]) / va[i - 1] + ta[i - 1];
	}
	tb[n + 1] = 0;vb[n + 1] = 1;
	for(int i = n;i >= 0;--i)
	{
		vb[i] = vb[i + 1] + 1;
		tb[i] = double(a[i + 1] - a[i]) / vb[i + 1] + tb[i + 1];
	}
	for(int i = 1;i <= n;++i)
	{
		if(fabs(ta[i] - tb[i]) <= 1e-6)
		{
			printf("%.20lf\n",ta[i]);
			return;
		}
	}
	int pos = -1;
	for(int i = 0;i <= n;++i)
	{
		if(ta[i] <= tb[i] && ta[i + 1] >= tb[i + 1])
		{
			pos = i;
			break;
		}
	}
	printf("%.20lf\n",(a[pos + 1] - a[pos] + ta[pos] * va[pos] + tb[pos + 1] * vb[pos + 1]) / (va[pos] + vb[pos + 1]));
	return;
}
int main()
{
	int testcases;
	scanf("%d",&testcases);
	while(testcases--)work();
	return 0;
}

D:

对每个向上走的长度记录 \(l[i]\) 表示要向右走多少才行,然后会发现可以前缀最大值做。

#include<bits/stdc++.h>
using namespace std; 
int n,m;
#define MAXN 2010
#define INF 0x3f3f3f3f
int a[MAXN],b[MAXN],c[MAXN],d[MAXN];
struct move
{
	int u,r;
}s[MAXN];
int up[1000010];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i)scanf("%d%d",&a[i],&b[i]);
	for(int i = 1;i <= m;++i)scanf("%d%d",&c[i],&d[i]);
	for(int i = 1;i <= n;++i)
	{
		for(int j = 1;j <= m;++j)
		{
			if(c[j] >= a[i] && d[j] >= b[i])
			{
				up[c[j] - a[i]] = max(up[c[j] - a[i]],d[j] - b[i] + 1);
			}
		}
	}
	for(int i = 1000000;i >= 0;--i)
	{
		up[i] = max(up[i],up[i + 1]);
	}
	int ans = 0x3f3f3f3f;
	for(int i = 0;i <= 1000001;++i)
	{
		ans = min(ans,i + up[i]);
	}
	cout << ans << endl;
	return 0;
}

E:

把团内的每个点向团所代表的点连边,然后会发现没有 \(rainbow\) 等价于这个图没有环。

#include<bits/stdc++.h>
using namespace std;
int n,m;
#define MAXN 100010
int a[MAXN],b[MAXN];
namespace KRU
{
	struct edge{int u,v,w;}e[MAXN << 1];
	int edgenum = 0;
	void add(int a,int b,int w){e[++edgenum] = (edge){a,b,w};return;}
	bool cmp(edge a,edge b){return a.w > b.w;}
	int f[MAXN << 1];
	int find(int x){return (f[x] == x ? x : f[x] = find(f[x]));}
	long long kruskal()
	{
		long long ans = 0;
		sort(e + 1,e + 1 + edgenum,cmp);
		for(int i = 1;i <= n + m;++i)f[i] = i;
		for(int i = 1;i <= edgenum;++i)
		{
			int p = find(e[i].u),q = find(e[i].v);
			if(p == q)continue;
			ans += e[i].w;
			f[p] = q;
		}
		return ans;
	}
}
int main()
{
	scanf("%d%d",&m,&n);
	for(int i = 1;i <= m;++i)scanf("%d",&a[i]);
	for(int i = 1;i <= n;++i)scanf("%d",&b[i]);
	long long ans = 0;
	for(int i = 1;i <= m;++i)
	{
		int s;scanf("%d",&s);
		int v;
		for(int k = 1;k <= s;++k)
		{
			scanf("%d",&v);
			KRU::add(i + n,v,a[i] + b[v]);//cout << " : " << i << " " << v << " " << a[i] << " " << b[v] << " " << a[i] + b[v] << endl;
			ans += a[i] + b[v];
		}
	}
	ans -= KRU::kruskal();
	cout << ans << endl;
	return 0;
}

F:

发现可以分治让 \(2^n\) 长度的区间变成一个数,那么就对 \([1,l]\)\([n-l+1,n]\) 都做一遍就行了。

#include<bits/stdc++.h>
using namespace std;
int n;
vector< pair<int,int> > ans;
void solve(int l,int r)
{
	if(l == r)return;
	int mid = (l + r) >> 1;
	solve(l,mid);solve(mid + 1,r);
	for(int i = l;i <= mid;++i)ans.push_back(make_pair(i,mid + 1 + i - l));
	return;
}
int main()
{
	cin >> n;
	int l = 1;
	while(l * 2 <= n)l = l * 2;
	solve(1,l);solve(n - l + 1,n);
	cout << ans.size() << endl;
	for(int i = 0;i < ans.size();++i)printf("%d %d\n",ans[i].first,ans[i].second);
	return 0;
}	

G:

团内边小于团之间的边,一个团可以增加一,连接两个连通块就卷积,相当于树形 \(dp\)\(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
#define MOD 998244353
inline int read()
{
	int res = 0;
	char c = getchar();
	while(!isdigit(c))c = getchar();
	while(isdigit(c))
	{
		res = (res << 1) + (res << 3) + c - '0';
		c = getchar();
	}
	return res;
}
int n;
#define MAXN 1510
struct edge
{
	int u,v,w;
}es[MAXN * MAXN];
int en = 0;
int f[MAXN];
int ecnt[MAXN];
int siz[MAXN];
int find(int x){return (x == f[x] ? x : f[x] = find(f[x]));}
bool cmp(edge a,edge b){return a.w < b.w;}
int dp[MAXN][MAXN];
int g[MAXN];
void merge(int a,int b)
{
	for(int i = 1;i <= siz[a] + siz[b];++i)g[i] = 0;
	for(int i = 1;i <= siz[a];++i)
		for(int j = 1;j <= siz[b];++j)
			g[i + j] = (g[i + j] + 1ll * dp[a][i] * dp[b][j] % MOD) % MOD;
	for(int i = 1;i <= siz[a] + siz[b];++i)dp[a][i] = g[i];
	return;
}
int main()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;++i)f[i] = i,siz[i] = 1;
	for(int i = 1;i <= n;++i)for(int j = 1;j <= n;++j)es[++en] = (edge){i,j,read()};
	sort(es + 1,es + 1 + en,cmp);
	for(int i = 1;i <= en;++i)
	{
		int p = find(es[i].u),q = find(es[i].v);
		if(p == q)++ecnt[p];
		else
		{
			if(siz[p] < siz[q])swap(p,q);
			merge(p,q);
			f[q] = p;
			siz[p] += siz[q];
			ecnt[p] += ecnt[q] + 1;
		}
		if(ecnt[p] == siz[p] * siz[p])dp[p][1] = 1;
	}
	int root = find(1);
	for(int i = 1;i <= n;++i)printf("%d ",dp[root][i]);
	return 0;
}

H:

设零的个数为 \(z\) ,则答案不超过 \(\lfloor\frac z2\rfloor\) ,那么将序列划为两半 \(L\)\(R\) ,满足对于 \(x\in L\) 右边有大于 \(\lfloor\frac z2\rfloor\)\(0\)\(R\) 同理,那么可以发现每种颜色只有 \(L\) 中最右边的和 \(R\) 中最左边的有用。那么可以网络流,\(S\) 连颜色,\(L\) 中每个点往左连,\(R\) 中每个点往右连,颜色连两个有用的点,零连 \(T\) ,分析一下,如果一个颜色不割,那么他的前缀零后缀零一定都被割了,那最后一定是割了前缀零,后缀零,还有不是包含在前缀后缀的,于是扫描线加线段树统计。

#include<bits/stdc++.h>
using namespace std;
int n;
#define MAXN 500010
int a[MAXN];
int cnt0;
struct match{int l,r;}ma[MAXN];
int sum0l[MAXN],sum0r[MAXN];
struct node
{
	int lc,rc;
	int minv,tag;
}t[MAXN << 1];
int ptr = 0;
int newnode(){return ++ptr;}
int root;
#define mid ((l + r) >> 1)
void build(int &rt,int l,int r)
{
	rt = newnode();
	if(l == r){t[rt].minv = sum0r[l] + n;return;}
	build(t[rt].lc,l,mid);
	build(t[rt].rc,mid + 1,r);
	t[rt].minv = min(t[t[rt].lc].minv,t[t[rt].rc].minv);
	return;
}
void pushdown(int rt)
{
	t[t[rt].lc].minv += t[rt].tag;t[t[rt].lc].tag += t[rt].tag;
	t[t[rt].rc].minv += t[rt].tag;t[t[rt].rc].tag += t[rt].tag;
	t[rt].tag = 0;
	return;
}
void add(int rt,int L,int R,int v,int l,int r)
{
	if(L <= l && r <= R)
	{
		t[rt].minv += v;
		t[rt].tag += v;
		return;
	}
	pushdown(rt);
	if(L <= mid)add(t[rt].lc,L,R,v,l,mid);
	if(R > mid)add(t[rt].rc,L,R,v,mid + 1,r);
	t[rt].minv = min(t[t[rt].lc].minv,t[t[rt].rc].minv);
	return;
}
int query(int rt,int L,int R,int l,int r)
{
	if(L <= l && r <= R)return t[rt].minv;
	int res = 0x3f3f3f3f;
	if(L <= mid)res = min(res,query(t[rt].lc,L,R,l,mid));
	if(R > mid)res = min(res,query(t[rt].rc,L,R,mid + 1,r));
	return res;
}
vector<int> seg[MAXN];
void work()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
	cnt0 = 0;
	for(int i = 1;i <= n;++i)cnt0 += (a[i] == 0);
	int s = (cnt0 + 1) / 2;
	for(int i = 1;i <= n;++i)ma[i].l = 0,ma[i].r = n + 1;
	sum0l[0] = sum0r[n + 1] = 0;
	for(int i = 1;i <= n;++i)sum0l[i] = sum0l[i - 1] + (a[i] == 0);
	for(int i = n;i >= 1;--i)sum0r[i] = sum0r[i + 1] + (a[i] == 0);
	int bor;
	for(int i = 1,c0 = 0;i <= n;++i)
	{
		if(a[i] == 0)++c0;
		if(c0 == s){bor = i;break;}
	}
	for(int i = bor;i >= 1;--i)if(a[i] && ma[a[i]].l == 0)ma[a[i]].l = i;
	for(int i = bor;i <= n;++i)if(a[i] && ma[a[i]].r == n + 1)ma[a[i]].r = i;
	for(int i = 0;i <= n + 1;++i)seg[i].clear();
	for(int i = 1;i <= n;++i)seg[ma[i].l].push_back(ma[i].r);
	ptr = 0;
	build(root,bor,n + 1);
	int ans = 0x3f3f3f3f;
	for(int l = 0;l <= bor;++l)
	{
		for(vector<int>::iterator it = seg[l].begin();it != seg[l].end();++it)add(root,bor,*it,-1,bor,n + 1);
		ans = min(ans,query(root,bor,n + 1,bor,n + 1) + sum0l[l]);
	}
	printf("%d\n",min(ans,cnt0 / 2));
	for(int i = 1;i <= ptr;++i)t[i].lc = t[i].rc = t[i].minv = t[i].tag = 0;
	return;
}
int main()
{
	int testcases = 0;
	scanf("%d",&testcases);
	while(testcases--)work();
	return 0;
}

I:

咕咕咕

 posted on 2020-10-08 21:25  15101051  阅读(94)  评论(0编辑  收藏  举报