本场链接: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
",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] = sumlimits_{k = 1}^i f[i - 1][k]),若(H_i < H_{i-1}),则(f[i][j] = sumlimits_{k = j}^i f[i - 1][k]),若(H_i > H_{i-1}),则(f[i][j] = sumlimits_{k = 1}^{j - 1} f[i - 1][k]).当(j == k)时,认为是(H_i)插入到(H_{i-1})的前面.
- 出口:(ans = sumlimits_{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
",res);
return 0;
}