本场链接:AtCoder Beginner Contest 209
C - Not Equal
不难注意到:\(A_i\)的次序无关,因为每个元素都不同,只需要考虑每个元素在他的区间内的取值即可.因此按上升对\(C_i\)排序,由于整个数组成上升,所以当做到\(C_i\)的时候,上限相当于去掉了\(i - 1\)个元素,如此即可统计答案.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 2e5+7,MOD = 1e9 + 7;
int c[N];
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&c[i]);
sort(c + 1,c + n + 1);
int res = 1;
forn(i,1,n) res = 1ll * res * max(0,c[i] - i + 1) % MOD;
printf("%d\n",res);
return 0;
}
D - Collision
弱智题,两个点会走到一个点是距离为偶数,反之是奇数.求树上任意两点距离即可.
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+7,M = 4e5+7,LIM = 17;
int edge[M],succ[M],cost[M],ver[N],idx;
int n,m,hh,tt,q[M];
int depth[N],fa[N][LIM + 3],dist[N];
void add(int u,int v,int w)
{
edge[idx] = v;
cost[idx] = w;
succ[idx] = ver[u];
ver[u] = idx++;
}
void bfs()
{
memset(depth,0x3f,sizeof depth);
memset(dist,0x3f,sizeof dist);
depth[1] = 0;depth[0] = -1;q[++tt] = 1;dist[1] = 0;
while(hh <= tt)
{
int u = q[hh++];
for(int i = ver[u];~i;i = succ[i])
{
int v = edge[i];
if(depth[v] > depth[u] + 1)
{
dist[v] = dist[u] + cost[i];
depth[v] = depth[u] + 1;
fa[v][0] = u;
q[++tt] = v;
for(int k = 1;k <= LIM;++k)
fa[v][k] = fa[fa[v][k - 1]][k - 1];
}
}
}
}
int lca(int x,int y)
{
if(depth[x] < depth[y]) swap(x,y);
for(int i = LIM;i >= 0;--i)
if(depth[fa[x][i]] >= depth[y])
x = fa[x][i];
if(x == y) return x;
for(int i = LIM;i >= 0;--i)
if(fa[x][i] != fa[y][i])
x = fa[x][i],y = fa[y][i];
return fa[x][0];
}
int main()
{
memset(ver,-1,sizeof ver);
scanf("%d%d",&n,&m);
for(int i = 1;i < n;++i)
{
int u,v;scanf("%d%d",&u,&v);
add(u,v,1);add(v,u,1);
}
bfs();
while(m--)
{
int u,v;scanf("%d%d",&u,&v);
if((dist[u] + dist[v] - 2 * dist[lca(u,v)]) % 2 == 0) puts("Town");
else puts("Road");
}
return 0;
}
E - Shiritori
首先优化建图:如果直接建图整个图的边数会达到\(N^2\)无法承受.将所有首尾三个元素相同的元素合并到一个点,边由每个\(s_i\)决定:首对应的点是\(u\)尾对应的点是\(v\)则建\(u->v\)的边.
考虑求答案:如果这张图上没有环显然是可以直接递推的:在反图上跑拓扑排序即可.但是有环其实也不影响,最终所有没有确定状态的点都是平局.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
const int N = 52 * 52 * 52 + 7,M = 2 * N,C = 2e5+7;
char s[10];
vector<pii> edges(C);
vector<int> E[N];
int deg[N],ans[N];
inline int gpos(char a)
{
if(a >= 'A' && a <= 'Z') return a - 'A';
return a - 'a' + 26;
}
inline int gid(char a,char b,char c)
{
return gpos(a) * 52 * 52 + gpos(b) * 52 + gpos(c);
}
int main()
{
int n;scanf("%d",&n);
forn(i,1,n)
{
scanf("%s",s + 1);int len = strlen(s + 1);
edges[i] = {gid(s[1],s[2],s[3]),gid(s[len - 2],s[len - 1],s[len])};
++deg[edges[i].x];
E[edges[i].y].push_back(edges[i].x);
}
queue<int> q;memset(ans,-1,sizeof ans);
forn(i,0,N - 1) if(!deg[i]) q.push(i),ans[i] = 0;
while(!q.empty())
{
int u = q.front();q.pop();
for(auto& v : E[u])
{
if(ans[v] != -1) continue;
--deg[v];
if(ans[u] == 0) ans[v] = 1,q.push(v);
else if(!deg[v]) ans[v] = 0,q.push(v);
}
}
forn(i,1,n)
{
if(ans[edges[i].y] == -1) puts("Draw");
else if(ans[edges[i].y] == 0) puts("Takahashi");
else puts("Aoki");
}
return 0;
}
F - Deforestation
这个题如果只是要求最小值,是一个出烂的玩意,但是要求方案数,手玩一下发现原来求最小值的贪心对于求方案数没有任何贡献.
考虑从题目本身做:对于每个\(H_i\)对于答案的贡献是多少?当删去\(H_i\)的时候一定会产生\(H_i\)的贡献,当删去\(H_{i+1}\)的时候,\(H_i\)会产生贡献当且仅当\(H_{i+1}\)是先于\(H_i\)被砍的.同理当删去\(H_{i-1}\)的时候,\(H_i\)会产生贡献当且仅当\(H_{i-1}\)先于\(H_i\)被删掉.
从前往后考虑每个元素\(i \in [1,n-1]\):
- 若\(H_i < H_{i+1}\),那么\(H_i\)的删去时间应该晚于\(H_{i+1}\),否则会让\(H_{i+1}\)往答案贡献两次而不是\(H_i\)贡献两次(显然后者更优).
- 若\(H_i > H_{i+1}\),那么\(H_i\)的删去时间应该早于\(H_{i+1}\),原因同上.
- 若两者相同,显然先后不会再对贡献有影响.
如此可以考虑一个dp
:
- 状态:\(f[i][j]\)表示分配前\(i\)个元素,末尾元素被安排在\(j\)位置上的方案数.
- 入口:\(f[1][1] = 1\)其他为\(0\).
- 转移:对于\(i \in[2,n]\),若\(H_i == H_{i-1}\)则\(f[i][j] = \sum\limits_{k = 1}^i f[i - 1][k]\),若\(H_i < H_{i-1}\),则\(f[i][j] = \sum\limits_{k = j}^i f[i - 1][k]\),若\(H_i > H_{i-1}\),则\(f[i][j] = \sum\limits_{k = 1}^{j - 1} f[i - 1][k]\).当\(j == k\)时,认为是\(H_i\)插入到\(H_{i-1}\)的前面.
- 出口:\(ans = \sum\limits_{k = 1}^n f[n][i]\).
转移部分,在每次处理完\(i\)维的转移后,记录本维状态的前缀和,即可将转移复杂度降到可以承受的\(O(n^2)\).
注意给\(sum[1] = 1\)初值.
($j == k的情况如何处理有些怪,以后补)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
const int N = 4005,MOD = 1e9+7;
int H[N],f[N][N],sum[N];
int main()
{
int n;scanf("%d",&n);
forn(i,1,n) scanf("%d",&H[i]);
f[1][1] = sum[1] = 1;
forn(i,2,n)
{
forn(j,1,i)
{
if(H[i] == H[i - 1]) f[i][j] = sum[i - 1];
else if(H[i] < H[i - 1]) f[i][j] = ((sum[i - 1] - sum[j - 1]) % MOD + MOD) % MOD;
else f[i][j] = sum[j - 1];
}
forn(j,1,n) sum[j] = (f[i][j] + sum[j - 1]) % MOD;
}
int res = 0;
forn(i,1,n) res = (res + f[n][i]) % MOD;
printf("%d\n",res);
return 0;
}