// 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 ;
}

B - Phillip and Trains

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 ;
}

E - Igor In the Museum

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 ;
}

G - New Year Permutation

对连通部分排序就完事了

#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 ;
}

N - News Distribution

并查集

#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次样例。。。尝试两种方法写)

A - Por Costel and Azerah

子序列里和为偶数的个数,简单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 ;
}

C - Por Costel and Bujor

D - Por Costel and the Censorship Committee

E - Por Costel and the Cipher

F - Por Costel and the Alien Invasion

G - Por Costel and the Orchard

H - Por Costel and the Match

并查集裸题(可以用带权并查集做?)

#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 ;
}

I - Por Costel and the Match

求[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;
}

J - Por Costel and Pinball

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

E - Jamie and Alarm Snooze

简单模拟题。

#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 全记录的更多相关文章

  1. 2020 CCPC Wannafly Winter Camp Day1 C. 染色图

    2020 CCPC Wannafly Winter Camp Day1 C. 染色图 定义一张无向图 G=⟨V,E⟩ 是 k 可染色的当且仅当存在函数 f:V↦{1,2,⋯,k} 满足对于 G 中的任 ...

  2. 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 ...

  3. Wannafly Winter Camp 2019.Day 8 div1 I.岸边露伴的人生经验(FWT)

    题目链接 \(Description\) 给定\(n\)个十维向量\(\overrightarrow{V_i}=x_1,x_2,...,x_{10}\).定义\(\overrightarrow{V}= ...

  4. Wannafly Winter Camp 2019.Day 8 div1 E.Souls-like Game(线段树 矩阵快速幂)

    题目链接 \(998244353\)写成\(99824435\)然后调这个线段树模板1.5h= = 以后要注意常量啊啊啊 \(Description\) 每个位置有一个\(3\times3\)的矩阵, ...

  5. CCPC Wannafly Winter Camp Div2 部分题解

    Day 1, Div 2, Prob. B - 吃豆豆 题目大意 wls有一个\(n\)行\(m\)列的棋盘,对于第\(i\)行第\(j\)列的格子,每过\(T[i][j]\)秒会在上面出现一个糖果, ...

  6. 2020 CCPC Wannafly Winter Camp Day1 Div.1&amp F

    #include<bits/stdc++.h> #define forn(i, n) for (int i = 0; i < int(n); i++) #define fore(i, ...

  7. 2020 CCPC Wannafly Winter Camp Day2-K-破忒头的匿名信

    题目传送门 sol:先通过AC自动机构建字典,用$dp[i]$表示长串前$i$位的最小代价,若有一个单词$s$是长串的前$i$项的后缀,那么可以用$dp[i - len(s)] + val(s)$转移 ...

  8. 2020 CCPC Wannafly Winter Camp Day1-F-乘法

    题目传送门 sol:二分答案$K$,算大于$K$的乘积有多少个.关键在于怎么算这个个数,官方题解上给出的复杂度是$O(nlogn)$,那么计算个数的复杂度是$O(n)$的.感觉写着有点困难,自己写了一 ...

  9. 2019 wannafly winter camp day 3

    2019 wannafly winter camp day 3 J 操作S等价于将S串取反,然后依次遍历取反后的串,每次加入新字符a,当前的串是T,那么这次操作之后的串就是TaT.这是第一次转化. 涉 ...

随机推荐

  1. 【python】遇到的错误

    呃.这学期在学python啦.之前虽然自学过,但都是跟着教程也没使用什么编译环境.没遇到奇奇怪怪的错误. 现在就当作一个记录贴吧. 用的编译工具是pycharm.电脑是MacBook Air 1.我在 ...

  2. importError:cannot import name imsave/imread等模块

    首先要先看相应的库是否已经安裝成功 pip install numpy pip install pillow pip install scipy 都成功安装之后,执行: import scipy.mi ...

  3. Activiti学习笔记1 — 下载与开发环境的配置

    一.下载 JDK下载URL: Tomcat下载URL:http://tomcat.apache.org/ Eclipse下载URL:http://www.oracle.com/technetwork/ ...

  4. 2018-10-29-微软-Tech-Summit-技术暨生态大会课程-·-基于-Roslyn-打造高性能预编译框架...

    title author date CreateTime categories 微软 Tech Summit 技术暨生态大会课程 · 基于 Roslyn 打造高性能预编译框架 lindexi 2018 ...

  5. Hive中SQL查询转换成MapReduce作业的过程

  6. css---2D变形

    1.transfrom:rotate(360deg); 用前要加transition: 2s;                      deg重点 transform:rotate(angle); ...

  7. flask 使用hashlib加密

    flask 使用hashlib加密 import hashlib #引入hashlib #使用方法: password = ' sha1 = hashlib.sha1() #使用sha1加密方法,你还 ...

  8. csps模拟83最大异或和简单的括号序列旅行计划题解

    题面:https://www.cnblogs.com/Juve/articles/11733280.html 最大异或和: 简单博弈,小Q一定不会输,如果异或和为0,则平局,因为无论小Q如何拿,小T都 ...

  9. bsgs+求数列通项——bzoj3122(进阶指南模板该进)

    /* 已知递推数列 F[i]=a*F[i-1]+b (%c) 解方程F[x]=t an+1 = b*an + c an+1 + c/(b-1) = b(an + c/(b-1)) an+1 + c/( ...

  10. 第四周——重新clone项目后maven问题

    重新clone项目后,一直报错,"类重复..." clean后install也无效果. 原因是idea在重启项目时会更改maven为默认的idea自带的maven配置,要重新设置