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:
咕咕咕