CCPC-Wannafly Summer Camp 2019 全记录
// 7.19-7.29 东北大学秦皇岛校区十天训练营,题目都挂在了Vjudge上。训练期间比较忙,没空更博总结,回来继续补题消化。
Day1
这天授课主题是简单图论,节奏挺好,wls两小时理完图论里的基本知识点。
下午的赛题就偏入门了(简单图论无疑),只涉及到最短路问题和简单的搜索以及一些奇怪的技巧。(差分约束呢?最小生成树呢?强连通分量呢?)
A - Jzzhu and Cities (补)
把火车线路加上跑Dijkstra就好了,标记火车线路,相等时也要push。在最短路上的火车线路不能被取消,剩下的全部能取消。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = ;
struct Edge {
int to;
bool istrain;
ll w;
Edge(int v, bool is, ll ww):to(v), istrain(is), w(ww){}
bool operator<(const Edge& a)const {
if(w==a.w) return istrain; // 非火车节点先更新
return w > a.w;
}
};
vector<Edge> G[maxn]; bool vis[maxn];
int d[maxn]; int Dijkstra() {
memset(d, 0x3f, sizeof(d));
memset(vis, , sizeof(vis));
d[] = ;
int res = ; priority_queue<Edge> q;
q.push(Edge(, , ));
while(!q.empty()) {
Edge tmp = q.top(); q.pop();
int u = tmp.to;
if(vis[u]) continue; vis[u] = ;
// d[u] = tmp.w;
if(tmp.istrain) ++res; for(int i=;i<G[u].size();i++) {
int v = G[u][i].to;
if(!vis[v] && d[v]>=d[u]+G[u][i].w) {
d[v] = d[u] + G[u][i].w;
q.push(Edge(v, G[u][i].istrain, d[v]));
}
} }
return res; } int main() {
int n, m, k;
cin>>n>>m>>k;
int u, v, w;
for(int i=;i<m;i++) {
scanf("%d %d %d", &u, &v, &w);
G[u].push_back(Edge(v, , w));
G[v].push_back(Edge(u, , w));
}
for(int i=;i<k;i++) {
scanf("%d %d", &v, &w);
G[].push_back(Edge(v, , w));
// G[v].push_back(Edge(1, 1, w));
} printf("%d\n", k-Dijkstra()); return ;
}
BFS 注意打标记!!!(虽然只是3*100的地图也要爆内存!)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int n, k, sx;
char mp[][];
bool vis[][];
struct node {
int x, y;
node(int _x, int _y):x(_x), y(_y) {}
}; bool check(int x, int y) {
if(x< || x>)
return false;
if(y>=n)
return true;
if(mp[x][y]=='.')
return true; return false;
} bool bfs() {
queue<node> q;
q.push(node(sx, )); while(q.size()) {
node now = q.front(); q.pop();
if(now.y>=n) {
return true;
} // printf("(%d,%d) -> ", now.x, now.y); int nx = now.x, ny = now.y+;
if(!check(nx, ny)) continue; // 向右走一步 for(int i=-;i<=;i++) { // 尝试三个方向移动
nx = now.x + i;
if(check(nx, ny) && check(nx, ny+) && check(nx, ny+) && !vis[nx][ny+]) {
q.push(node(nx, ny+));
vis[nx][ny+] = ;
}
}
}
return false;
} int main() {
int t; cin>>t;
while(t--) {
scanf("%d %d", &n, &k);
getchar();
memset(vis, , sizeof(vis));
for(int i=;i<;i++) {
scanf("%s", mp[i]); if(mp[i][]=='s')
sx = i; }
printf("%s\n", bfs()?"YES":"NO");
} return ;
}
C - A Mist of Florescence (补)
构造题,技巧就是设计井字形的连通块,把其他颜色块涂到井字的格子上。
#include<iostream>
#include<cstdio>
using namespace std; int a, b, c, d;
char ans[][];
void solve() {
for(int i=;i<=;i++) {
for(int j=;j<;j++) {
if(i%== && j%== && a) ans[i][j] = 'A', --a;
else ans[i][j] = 'D';
}
} for(int i=;i<=;i++) {
for(int j=;j<;j++) {
if(i%== && j%== && b) ans[i][j] = 'B', --b;
else ans[i][j] = 'D';
}
} --c;
--d;
for(int i=;i<=;i++) {
for(int j=;j<;j++) {
if(i%== && j%== && c) ans[i][j] = 'C', --c;
else ans[i][j] = 'D';
}
} for(int j=;j<;j++) {
ans[][j] = 'C';
} for(int i=;i<;i++) {
for(int j=;j<;j++) {
if(i%== && j%== && d) ans[i][j] = 'D', --d;
else ans[i][j] = 'C';
}
} }
int main() {
cin>>a>>b>>c>>d;
solve();
printf("49 49\n");
for(int i=;i<;i++) {
for(int j=;j<;j++)
printf("%c", ans[i][j]);
printf("\n");
} return ;
}
DFS到墙的边界 对每块编号!
#include<iostream>
#include<cstdio>
using namespace std; int n, m, k;
char mp[][];
int v[][], id; // v[i][j]: mp[i][j]的分类编号 id: 当前编号
int res[*]; // res[id]: 第id块的答案 const int dx[] = {, , , -};
const int dy[] = {, -, , }; int ans;
void dfs(int x, int y) {
if(mp[x][y]=='*') {
ans++;
return;
}
v[x][y] = id;
for(int i=;i<;i++) {
int nx = x+dx[i], ny = y+dy[i];
if(nx>= && nx<m && ny>= && ny<n && !v[nx][ny]) {
dfs(nx, ny);
}
}
}
int main() {
scanf("%d %d %d", &m, &n, &k);
getchar();
for(int i=;i<m;i++) {
scanf("%s", mp[i]);
} for(int i=;i<m;i++) {
for(int j=;j<n;j++) {
if(mp[i][j]=='.' && !v[i][j]) {
++id;
ans = ;
dfs(i, j);
res[id] = ans;
}
}
} while(k--) {
int x, y;
scanf("%d %d", &x, &y);
printf("%d\n", res[v[x-][y-]]);
} return ;
}
F - The Cild and Toy (补)
贪心!由于每去掉一个点,等价于去掉了所有与它相连的边,就是问去掉全部边的最小代价。答案当然就是每条边两个节点权值小的那头的总和。都不用建图!!
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = ;
int n, m;
int w[maxn]; int main() {
cin>>n>>m;
for(int i=;i<=n;i++) {
scanf("%d", &w[i]);
}
int u, v, ans = ;
for(int i=;i<m;i++) {
scanf("%d %d", &u, &v);
ans += min(w[u], w[v]);
}
printf("%d\n", ans); return ;
}
对连通部分排序就完事了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
int n, num[], id;
int mp[][];
bool vis[]; int ans[];
struct list {
vector<int> num;
vector<int> id;
}L[]; void dfs(int x, int id) {
vis[x] = ; L[id].num.push_back(num[x]);
L[id].id.push_back(x); for(int i=;i<=n;i++) {
if(mp[x][i]) {
if(!vis[i])
dfs(i, id);
}
}
} int main() {
cin>>n;
for(int i=;i<=n;i++) {
scanf("%d", &num[i]);
}
for(int i=;i<=n;i++) {
for(int j=;j<=n;j++) {
scanf("%1d", &mp[i][j]);
}
} for(int i=;i<=n;i++) {
if(!vis[i])
dfs(i, ++id);
} for(int i=;i<=id;i++) {
// for(int j=0;j<L[i].num.size();j++) {
// printf("%d:%d ", L[i].num[j], L[i].id[j]);
// }
// cout<<endl; sort(L[i].num.begin(), L[i].num.end());
sort(L[i].id.begin(), L[i].id.end()); for(int j=;j<L[i].num.size();j++) {
ans[L[i].id[j]] = L[i].num[j];
}
}
for(int i=;i<=n;i++) {
printf("%d%c", ans[i], i!=n?' ':'\n');
}
return ;
}
H - Alyona and the Tree (补)
从根开始dfs就好,每个节点为max(0LL, now+w[i])
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = ;
typedef long long ll; struct Edge {
int to;
ll w;
Edge(int v, ll ww):to(v), w(ww) {}
};
vector<Edge> G[maxn];
int n, vw[maxn];
int ans;
void dfs(int u, int fa, ll now) {
for(int i=;i<G[u].size();i++) {
int v = G[u][i].to;
if(v!=fa) {
if(vw[v]>=now+G[u][i].w) {
--ans;
// printf("%d->%d\n", u, v);
dfs(v, u, max(now+G[u][i].w, 0LL));
}
}
} } int main() {
cin>>n; ans = n;
for(int i=;i<=n;i++) {
scanf("%d", &vw[i]);
}
int u, w;
for(int i=;i<=n;i++) {
scanf("%d %d", &u, &w);
G[i].push_back(Edge(u, w));
G[u].push_back(Edge(i, w));
} dfs(, -, );
printf("%d\n", ans-);
return ;
}
L - Love Triangle
假的三元环??
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = ;
int n, tot;
int love[maxn], low[maxn];
bool vis[maxn];
bool ans;
void dfs(int u) {
if(ans) return; vis[u] = true;
low[u] = ++tot;
int v = love[u];
if(v && !vis[v]) {
dfs(v);
if(love[v] && low[love[v]]==low[u]+ && love[love[v]]==u) {
ans = true;
return;
}
}
} int main() {
cin>>n;
for(int i=;i<=n;i++) {
scanf("%d", &love[i]);
}
ans = false;
for(int i=;i<=n;i++) {
if(!vis[i] && !ans)
dfs(i);
}
printf("%s\n", ans?"YES":"NO");
return ;
}
并查集
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = ;
int n, m;
int fa[maxn];
int g[maxn], cnt[maxn]; int Find(int x) {
return fa[x]==x?x:(fa[x]=Find(fa[x]));
} void Union(int x, int y) {
int a = Find(x);
int b = Find(y);
if(a==b) return;
fa[a] = b;
} int main() {
cin>>n>>m;
for(int i=;i<=n;i++) fa[i] = i;
while(m--) {
int k;
scanf("%d", &k);
for(int i=;i<k;i++) {
scanf("%d", &g[i]);
}
for(int i=;i<k;i++) {
Union(g[], g[i]);
}
}
for(int i=;i<=n;i++) {
cnt[Find(i)]++;
}
for(int i=;i<=n;i++) {
printf("%d%c", cnt[Find(i)], i!=n?' ':'\n');
}
return ;
}
O - NP-Hard Problem (补)
二分图裸题。。。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = ;
int n, m;
vector<int> G[maxn];
int deg[maxn];
int id[maxn];
vector<int> ans[];
bool dfs(int u) {
for(int i=;i<G[u].size();i++) {
int v = G[u][i];
if(!id[v]) {
id[v] = - id[u];
if(id[v]==)
ans[].push_back(v);
else
ans[].push_back(v);
if(!dfs(v)) return false;
} else if(id[v]==id[u]) return false;
}
return true;
} int main() {
cin>>n>>m;
int u, v;
for(int i=;i<m;i++) {
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
++deg[u];
}
for(int i=;i<=n;i++) {
if(!deg[i]) continue; if(!id[i]) {
id[i] = ;
ans[].push_back(i);
if(!dfs(i))
return * printf("-1\n");
} } for(int k=;k<=;k++) {
printf("%d\n", ans[k].size());
for(int i=;i<ans[k].size();i++) {
printf("%d%c", ans[k][i], i==ans[k].size()-?'\n':' ');
}
} return ;
}
Day2
第二天的主题是简单数论,下午挂的是CF上的一场区域赛:2015 ACM National Contest Romania
注意文件读入要求!!!(第一题让我WA了4次样例。。。尝试两种方法写)
子序列里和为偶数的个数,简单dp(或组合数计算)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1e9+;
const int maxn = ;
int arr[maxn], n;
ll a[maxn], b[maxn]; int main() { freopen("azerah.in", "r", stdin);
freopen("azerah.out", "w", stdout);
int t; cin>>t;
while(t--) {
scanf("%d", &n);
for(int i=;i<=n;i++) {
scanf("%lld", &arr[i]);
}
a[] = b[] = ;
if(arr[]%==) b[] = ;
else a[] = ;
for(int i=;i<=n;i++) {
if(arr[i]%==) {
b[i] = (b[i-]*+) % mod;
a[i] = (a[i-]*)% mod;
}else {
b[i] = (a[i-] + b[i-]) %mod;
a[i] = (a[i-] + b[i-] + ) %mod;
}
// cout<<b[i]<<endl;
}
cout<<b[n]<<endl;
}
return ;
} /*
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1000100;
int arr[maxn], n; ll pow(ll a, ll n) {
ll res = 1;
while(n) {
if(n%2) {
res = res * a %mod;
}
a = a*a % mod;
n >>= 1;
}
return res;
}
int main() {
freopen("azerah.in", "r", stdin);
freopen("azerah.out", "w", stdout); int t; cin>>t;
while(t--) {
ll odd = 0, even = 0;
scanf("%d", &n);
for(int i=1;i<=n;i++) {
scanf("%lld", &arr[i]);
if(arr[i]%2==0) even++;
else odd++;
}
if(odd==0)
cout<<(pow(2, even)-1+mod)%mod<<endl;
else
cout<<(pow(2, odd-1)*pow(2, even)%mod-1+mod)%mod<<endl;
}
return 0;
} */
B - Por Costel and the Algorithm (补)
给了一段Bellman-Ford代码,调整边的顺序让它最多执行两次。
记录最短路上的边即可,dfs输出边的顺序。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = ; int n, m;
struct Edge{
int to;
int id;
ll w;
Edge(int v, int i, ll _w):to(v), id(i), w(_w) {}
bool operator<(const Edge& e)const {
return w > e.w;
}
};
vector<Edge> G[maxn]; ll d[maxn];
int e[maxn]; // e[v]: 连向v的边的编号
bool path[*maxn]; void Dijkstra() {
memset(d, 0x3f, sizeof(d));
d[] = ; priority_queue<Edge> q;
q.push(Edge(, , ));
while(!q.empty()) {
Edge tmp = q.top(); q.pop();
int u = tmp.to;
for(int i=;i<G[u].size();i++) {
int v = G[u][i].to;
if(d[v]>d[u]+G[u][i].w) {
d[v] = d[u] + G[u][i].w; path[e[v]] = ;
path[G[u][i].id] = ;
e[v] = G[u][i].id; q.push(Edge(v, , d[v]));
}
}
}
} void dfs(int u) {
for(int i=;i<G[u].size();i++) {
int m = G[u][i].id, v = G[u][i].to;
//printf("%d->%d %d", u, v, m);
if(path[m]) {
printf("%d ", m);
dfs(v);
}
}
} int main() {
freopen("algoritm.in", "r", stdin);
freopen("algoritm.out", "w", stdout);
int t; cin>>t;
while(t--) {
memset(e, , sizeof(e));
memset(path, , sizeof(path));
for(int i=;i<maxn;i++) G[i].clear(); scanf("%d %d", &n, &m);
for(int i=;i<=m;i++) {
int u, v; ll w;
scanf("%d %d %lld", &u, &v, &w);
G[u].push_back(Edge(v, i, w));
}
Dijkstra(); dfs();
for(int i=;i<=m;i++) {
if(!path[i])
printf("%d ", i);
}
printf("\n");
}
return ;
}
D - Por Costel and the Censorship Committee
F - Por Costel and the Alien Invasion
G - Por Costel and the Orchard
并查集裸题(可以用带权并查集做?)
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = ;
int n, m;
int fa[maxn*];
int Find(int x) {
return fa[x]==x?x:(fa[x]=Find(fa[x]));
} void Union(int x, int y) {
int a = Find(x), b= Find(y);
if(a!=b)
fa[a] = b;
} int main() {
freopen("meciul.in", "r", stdin);
freopen("meciul.out", "w", stdout);
int t; cin>>t;
while(t--) {
scanf("%d %d", &n, &m);
for(int i=;i<=*n;i++) fa[i] = i;
int x, y;
while(m--) {
scanf("%d %d", &x, &y);
if(Find(x)!=Find(y) && Find(x+n)!=Find(y+n)) {
printf("YES\n");
Union(x, y+n);
Union(x+n, y);
} else {
printf("NO\n");
}
}
}
return ;
}
求[n/i],分块(也可以只计算k=sqrt(n)的结果,ans*2 - k*k就是最终答案)
ll ans = 0;
for(int i=1;i<=n;) {
ans += n/i * (n/(n/i) - i +1);
i = n/(n/i) + 1;
}
K - Por Costel and the Firecracker (补)
没有内存限制的话就是水题,可是完全没有优化空间。。。
百度之,学会了分块打表
把第一次查询看成了x1,debug了半天。。。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = ;
const int mod = ;
ll n, a, b, x1, q, q1;
ll ans[maxn/+];
void pre() {
ans[] = x1;
ll last = x1;
for(ll i=;i<maxn;i++) {
last = (last*i % n + a) % n;
if(i%==) {
ans[i/] = last;
}
// cout<<i<<' '<<ans[i]<<endl;
} } ll cal(ll k) {
if(k%==) return ans[k/]; ll last = ans[k/];
for(ll i=k/*+;i<=k;i++) {
last = (last*i % n + a) % n;
}
return last;
} int main() {
freopen("pocnitoare.in", "r", stdin);
freopen("pocnitoare.out", "w", stdout); cin>>n>>a>>b>>x1>>q>>q1; pre();
printf("%lld\n", x1=cal(q1-)); // 第一次查询是q1-1而不是x1-1!!!
for(int i=;i<q;i++) {
q1 = (i*x1 % mod + b) % mod + ;
printf("%lld\n", x1=cal(q1-)); }
return ;
}
L - Por Costel and the Semipalindromes
WA了半天,原来是1左移出现了溢出,ll范围一定要写成 (1LL<<k)
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
char res[];
int tmp[];
int main() {
freopen("semipal.in", "r", stdin);
freopen("semipal.out", "w", stdout);
int t; cin>>t;
while(t--) {
int n; ll k;
scanf("%d %lld", &n, &k); res[] = res[n] = 'a'; if(k>(1LL<<(n-))) {
k -= (1LL<<(n-));
res[] = res[n] = 'b';
}
--k;
for(int i=;i<n-;i++) {
tmp[n--i] = (k>>i)&;
}
for(int j=;j<n;j++) {
if(tmp[j]) {
res[j] = 'b';
} else {
res[j] = 'a';
}
} for(int i=;i<=n;i++) {
putchar(res[i]);
}
puts("");
}
return ;
}
Day3
题目来自于NAIPC 2016北美邀请赛。
E - K-Inversions
给定一个只含有AB的字符串(长度不超过1000000),求间隔为1~n-1的B-A有多少对。
FFT模板题
F - Mountain Scenes
给长度总和为n的木条以及相框的宽度w和高度h,问能组成多少种不同的山峰形状。
大家的签到dp题,我的自闭题。。。 j 没有从0开始样例死都调不出来 噗
也就是dp[i][0] = 1
dp[i][j] 为宽度 i 用了总长 j 的木条的方案数。 dp[i+1][j] = sum( dp[i][j-k] ) ( 0<=k<=h, j )
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = ;
const int mod = 1e9+; int n, w, h;
ll dp[][maxn];
int main() { cin>>n>>w>>h;
// for(int i=0;i<=h && i<=w;i++) dp[i][0] = 1;
dp[][] = ;
for(int i=;i<=w;i++) {
for(int j=;j<=n;j++) { // j=0 开始。。。坑死自己了
for(int k=;k<=h && k<=j;k++) {
dp[i][j] = (dp[i][j] + dp[i-][j-k]) % mod;
}
}
} ll ans = ;
for(int i=;i<=n;i++)
ans = (ans + dp[w][i])%mod; cout<<(ans-min(n/w, h))%mod<<endl;
return ;
}
G - Symmetry
平面上有 n 个点( n<=1000),问至少加入多少点,使全部点与某一个点中心对称,或者全部点关于某条过其中两点的直线对称。
计算几何。
对于要求1,枚举全部点再check剩下点是否中心对称,复杂度O(n^2logn),可行。
对于要求2,枚举两点所得直线再check剩下点是否关于直线对称,复杂度O(n^3logn),TLE。
看题解好像是把直线去重才能过。
I - Tourists
求树上所有整除关系的两对节点的最短路之和。
计算所有符合条件的节点复杂度O(nlogn),树上两点 u, v 的最短路为 dep[u] + dep[v] - 2*dep[lca(u, v)] + 1。
找了个LCA的板子就1A了 O.O (第四个板子是改进的树链剖分求LCA,复杂度O(n + mlogn),是其中最快的。)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = ;
struct Edge {
int to, next;
}edges[maxn];
int head[maxn], tot; int fa[maxn], siz[maxn], dep[maxn], top[maxn];
// top[u] == u : u不是重儿子
// top[u] == fa: fa的重儿子是u
void add(int u, int v) {
edges[++tot].to = v;
edges[tot].next = head[u];
head[u] = tot;
} void dfs(int u) { // 求fa, siz, dep, top
int maxSon = , son = ;
top[u] = u;
siz[u] = ;
for(int i=head[u];i;i=edges[i].next) {
int v = edges[i].to;
if(v==fa[u]) continue; fa[v] = u;
dep[v] = dep[u] + ;
dfs(v);
siz[u] += siz[v]; if(siz[v]>maxSon)
maxSon = siz[son=v];
}
if(son) // 重儿子
top[son] = u;
} int Find(int u) {
return u==top[u]?u:top[u]=Find(top[u]);
} int LCA(int u, int v) {
if(Find(u)!=Find(v))
return dep[top[u]]<dep[top[v]] ? LCA(u, fa[top[v]]):LCA(v, fa[top[u]]);
else
return dep[u]<dep[v] ? u:v;
} int main() {
int n;
cin>>n;
for(int i=;i<n-;i++) {
int u, v;
scanf("%d %d", &u, &v);
add(u, v);
add(v, u);
}
memset(fa, -, sizeof(fa));
dfs(); long long ans = ;
for(int i=;i<=n/;i++) {
for(int j=i*;j<=n;j+=i) {
ans += dep[i]+dep[j]-*dep[LCA(i, j)] + ;
}
}
printf("%lld\n", ans);
return ;
}
Day4
A - One-dimensional Japanese Crossword
简单模拟题。
B - Passwords
简单数学题。
C - Journey
简单模拟题。
#include<iostream>
#include<cstdio>
using namespace std; bool check(int h, int m) {
// cout<<h<<":"<<m<<endl;
if(h%== || m%==) return true;
return false;
}
int main()
{
int x, h, min;
scanf("%d", &x);
scanf("%d %d", &h, &min); int y = ;
while(!check(h, min)) {
if(x==) {
if(--h<) h = ;
} else {
min -= x;
if(min<) {
min += ;
if(--h<) h = ;
}
}
++y;
} printf("%d\n", y);
return ;
}
F - Jamie and Binary Sequence (changed after round)
给定 n 和 k,将 n 分解成 k 个二次幂之和,求2的指数最小且字典序最大的一组解,不存在则输出 No。
思维题。
把 n 转成二进制,要保证最高次幂最小,当二进制位1个数小于k时,把最高位2^n拆成 2^(n-1) + 2^(n-1),这样多了一个1。
当拆分之后1的数量大于k时,要保证字典序最大,再从最低位开始拆分,每次只拆一个1,新的低位加2。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll; ll n;
int k, bits[], cnt, high, len;
int tmp[]; int main()
{
cin>>n>>k;
ll nn = n;
for(int i=;nn;i++) {
tmp[i] = nn&;
nn >>= ;
len = i;
if(tmp[i]) ++cnt;
}
for(int i=len;i>=;i--) {
bits[len-i] = tmp[i];
}
high = len; // 记录最高位指数
// for(int i=0;i<=len;i++) cout<<bits[i]; if(cnt>k) return * printf("No\n"); int pos = ;
while(cnt<k) {
if(cnt+bits[pos]<=k) { // 最小化y=max(ai)
cnt += bits[pos];
bits[pos+] += *bits[pos];
bits[pos] = ;
++pos;
len = max(pos, len); // 忘记更新,WA66组
} else {
pos = len;
while(!bits[pos]) --pos;
while(cnt<k) {
++cnt;
bits[pos] -= ;
bits[++pos] += ; }
}
} printf("Yes\n");
int up = max(pos, len);
for(int i=;i<=up;i++) {
for(int j=;j<bits[i];j++)
printf("%d ", high-i);
}
return ;
}
G - Jamie and Interesting Graph
需要构造出一个n个节点m条边的图,满足最小生成树的权为素数,1到n的最短路为素数。
构造1~n的一条链为最小生成树,权为p = 100019。剩下不停加边,边权为2p。
#include<iostream>
#include<cstdio>
using namespace std;
int p = ;
int main() {
int n, m;
cin>>n>>m;
printf("%d %d\n", p, p);
printf("1 2 %d\n", p-(n-));
for(int i=;i<n;i++)
printf("%d %d 1\n", i, i+);
m -= n-; for(int i=;i<n && m;i++)
for(int j=i+;j<=n && m;j++) {
printf("%d %d %d\n", i, j, *p);
--m; } return ;
}
H - 树上染色
很经典的树形dp。
// f[u][j]: 以u为根节点,涂了 j 个黑点的子树对答案的最大贡献
对于一条边,左边子树有 x 个黑点,则右边有 k-x 个黑点,左边白点为 size[v] - x,右边白点为 n - size[v] - (k-x),按照题意计算这部分贡献 val。
状态转移方程为 f[u][j] = max( f[u][j], f[u][j-x] + f[v][x] + val),j 倒序枚举更新。
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))
using namespace std;
typedef long long ll;
const int maxn = ;
struct Edge {
int u, w;
Edge(int uu, int ww):u(uu), w(ww) {}
};
vector<Edge> G[maxn];
int siz[maxn], n, kk;
ll f[maxn][maxn]; // f[u][j]: 节点i含有j个黑点的子树对答案的最大贡献 void dfs(int u, int fa) {
f[u][] = f[u][] = ;
siz[u] = ;
for(int i=;i<G[u].size();i++) {
int v = G[u][i].u;
if(v==fa) continue; dfs(v, u);
siz[u] += siz[v]; }
for(int i=;i<G[u].size();i++) {
int v = G[u][i].u, w = G[u][i].w;
if(v==fa) continue; for(int j=min(kk, siz[u]);j>=;j--) {
for(int k=;k<=min(j, siz[v]);k++) {
if(f[u][j-k]!=-) {
ll val = (1LL*k*(kk-k)+ 1LL*(siz[v]-k)*(n-kk-(siz[v]-k)))*w;
f[u][j] = max(f[u][j], f[u][j-k]+f[v][k]+val);
}
}
}
}
} int main() {
scanf("%d %d", &n, &kk);
for(int i=;i<n;i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
G[u].push_back(Edge(v, w));
G[v].push_back(Edge(u, w)); }
memset(f, -, sizeof(f));
dfs(, -);
printf("%lld\n", f[][kk]); return ;
}
Day5
Day6
CCPC-Wannafly Summer Camp 2019 全记录的更多相关文章
- 2020 CCPC Wannafly Winter Camp Day1 C. 染色图
2020 CCPC Wannafly Winter Camp Day1 C. 染色图 定义一张无向图 G=⟨V,E⟩ 是 k 可染色的当且仅当存在函数 f:V↦{1,2,⋯,k} 满足对于 G 中的任 ...
- 2020 CCPC Wannafly Winter Camp Day1 - I. K小数查询(分块)
题目链接:K小数查询 题意:给你一个长度为$n$序列$A$,有$m$个操作,操作分为两种: 输入$x,y,c$,表示对$i\in[x,y] $,令$A_{i}=min(A_{i},c)$ 输入$x,y ...
- Wannafly Winter Camp 2019.Day 8 div1 I.岸边露伴的人生经验(FWT)
题目链接 \(Description\) 给定\(n\)个十维向量\(\overrightarrow{V_i}=x_1,x_2,...,x_{10}\).定义\(\overrightarrow{V}= ...
- Wannafly Winter Camp 2019.Day 8 div1 E.Souls-like Game(线段树 矩阵快速幂)
题目链接 \(998244353\)写成\(99824435\)然后调这个线段树模板1.5h= = 以后要注意常量啊啊啊 \(Description\) 每个位置有一个\(3\times3\)的矩阵, ...
- CCPC Wannafly Winter Camp Div2 部分题解
Day 1, Div 2, Prob. B - 吃豆豆 题目大意 wls有一个\(n\)行\(m\)列的棋盘,对于第\(i\)行第\(j\)列的格子,每过\(T[i][j]\)秒会在上面出现一个糖果, ...
- 2020 CCPC Wannafly Winter Camp Day1 Div.1& F
#include<bits/stdc++.h> #define forn(i, n) for (int i = 0; i < int(n); i++) #define fore(i, ...
- 2020 CCPC Wannafly Winter Camp Day2-K-破忒头的匿名信
题目传送门 sol:先通过AC自动机构建字典,用$dp[i]$表示长串前$i$位的最小代价,若有一个单词$s$是长串的前$i$项的后缀,那么可以用$dp[i - len(s)] + val(s)$转移 ...
- 2020 CCPC Wannafly Winter Camp Day1-F-乘法
题目传送门 sol:二分答案$K$,算大于$K$的乘积有多少个.关键在于怎么算这个个数,官方题解上给出的复杂度是$O(nlogn)$,那么计算个数的复杂度是$O(n)$的.感觉写着有点困难,自己写了一 ...
- 2019 wannafly winter camp day 3
2019 wannafly winter camp day 3 J 操作S等价于将S串取反,然后依次遍历取反后的串,每次加入新字符a,当前的串是T,那么这次操作之后的串就是TaT.这是第一次转化. 涉 ...
随机推荐
- List<Map>中根据map的同一指标项数据——去重代码
先看网络上,博客经常出现的错误代码: for(ABatchAddCheckVO aBatchAddCheckVO : addList){ dto.put("aac001",aBat ...
- 2019牛客暑期多校训练营(第七场) E 线段树+离散化
题目传送门 题意:按照一定的公式给出若干个$<l,r>$,每次往一个序列中加上l到r的数字,并输出中位数. 思路:需要将每个$区间$离散化,比如把$[1,2]$变成$[1,3)$,也就是$ ...
- Virtualenv开发文档
virtualenv是创建孤立的Python环境的工具.正在解决的基本问题是依赖和版本之一以及间接权限.想象一下,您有一个需要LibFoo版本1的应用程序,但另一个应用程序需要版本2.如何使用这两个应 ...
- Algo: Basic
1. 二维数组的查找 2. 替换空格 3. 从尾到头打印链表 4. 重建二叉树 5. 用两个栈实现队列 6. 旋转数组的最小数字 7. 斐波那契数列 8. 跳台阶 9. 变态跳台阶 10. 矩阵覆盖 ...
- 代码执行批量Excel数据导入Oracle数据库
由于基于Oracle数据库上做开发,因此常常会需要把大量的Excel数据导入到Oracle数据库中,其实如果从事SqlServer数据库的开发,那么思路也是一样的,本文主要介绍如何导入Excel数据进 ...
- BZOJ 2660 (BJOI 2012) 最多的方案
Description 第二关和很出名的斐波那契数列有关,地球上的OIer都知道:F1=1, F2=2, Fi = Fi-1 + Fi-2,每一项都可以称为斐波那契数.现在给一个正整数N,它可以写成一 ...
- System.Web.Mvc.ActionResult.cs
ylbtech-System.Web.Mvc.ActionResult.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutral, Publi ...
- 怎么规划一个零基础学习Unity3D的“方法”或者“流程”?
具体出处:https://www.zhihu.com/question/35542990 我只是一个计算机相关专业毕业的,已经掌握了基础的C#并开发过.net的.目前突然心血来潮对unity3D有兴趣 ...
- Hdu-1452-Happy 2004-费马小定理推除法逆元+同余定理+积性函数
Consider a positive integer X,and let S be the sum of all positive integer divisors of 2004^X. Your ...
- 机器学习-线性回归算法(单变量)Linear Regression with One Variable
1 线性回归算法 http://www.cnblogs.com/wangxin37/p/8297988.html 回归一词指的是,我们根据之前的数据预测出一个准确的输出值,对于这个例子就是价格,回归= ...