博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Educational Codeforces Round 62(CF1140)
阅读量:5136 次
发布时间:2019-06-13

本文共 14778 字,大约阅读时间需要 49 分钟。

最近省队前联考被杭二成七南外什么的吊锤得布星,拿一场Div. 2恢复信心

然后Div.2 Rk3、Div. 1+Div. 2 Rk9,rating大涨200引起舒适

现在的Div. 2都怎么了,最难题难度都快接近3K了……

\(a_i\)的前缀最大值为\(Max_i\),那么要求的就是\(Max_i = i\)\(i\)的数量

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//This code is written by Itstusing namespace std;inline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a;}int main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin);//freopen("out","w",stdout);#endif int N = read() , maxN = 0 , cnt = 0; for(int i = 1 ; i <= N ; ++i){ maxN = max(maxN , read()); if(maxN == i) ++cnt; } cout << cnt; return 0;}

注意到如果字符串最右侧是一个'<'或者最左侧是一个'>'的话,我们只需要一直对最右边或者最左边的字符进行操作就可以满足条件

所以我们只需要在字符串首尾删去尽可能少的字符使得其最右侧是一个‘<’或者最左侧是一个'>'。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//This code is written by Itstusing namespace std;inline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a;}int main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout);#endif int T; for(cin >> T ; T ; --T){ string s; int len; cin >> len >> s; if(s[0] == '<' && s[s.size() - 1] == '>'){ int cnt1 = 0 , cnt2 = 0; for(int i = 0 ; i < s.size() && s[i] == '<' ; ++i) ++cnt1; for(int i = s.size() - 1 ; i >= 0 && s[i] == '>' ; --i) ++cnt2; cout << min(cnt1 , cnt2) << endl; } else cout << 0 << endl; } return 0;}

从大到小枚举选择的乐曲中最小的愉悦值,用一个堆维护愉悦值大于等于当前枚举值的所有乐曲的播放时间前\(k\)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//This code is written by Itstusing namespace std;#define int long longinline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a;}#define PII pair < int , int >#define st first#define nd secondbool p(PII a , PII b){return a.st > b.st;}signed main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout);#endif int N , K; cin >> N >> K; vector < PII > song; priority_queue < int , vector < int > , greater < int > > q; int sum = 0 , ans = 0; for(int i = 1 ; i <= N ; ++i){ int k = read() , b = read(); song.push_back(PII(b , k)); } sort(song.begin() , song.end() , p); for(auto t : song){ q.push(t.nd); sum += t.nd; if(q.size() > K){ sum -= q.top(); q.pop(); } ans = max(ans , t.st * sum); } cout << ans; return 0;}

可以证明最优的划分方案中,\(1\)与所有点都有一条边。所以直接计算\(\sum\limits_{i=2}^{n-1} i \times (i+1)\)即可

我觉得可以直接从样例看出这个结论

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//This code is written by Itstusing namespace std;#define int long longinline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a;}#define PII pair < int , int >#define st first#define nd secondbool p(PII a , PII b){return a.st > b.st;}signed main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout);#endif int N , sum = 0; cin >> N; for(int i = 3 ; i <= N ; ++i) sum = sum + i * (i - 1); cout << sum; return 0;}

既然不能存在长度\(>1\)的奇回文串,那么一定不能存在长度为\(3\)的回文串,也就意味着\(\forall i \in [1,n-2] , a_i \neq a_{i+2}\)

把下标为奇数和下标为偶数的\(a_i\)拿出来作为两个序列\(b_i,c_i\),那么我们只需要\(b_i \neq b_{i+1} , c_i \neq c_{i+1}\)就是合法的方案

对于两个序列分别dp,设\(f_{i,0/1}\)表示当前已经填完前\(i\)个数,且第\(i\)个数的值与\(i\)之后的第一个有限制的位置的值是否相等的方案数,转移分有限制的点和无限制的点。

边界情况有点多需要注意。

#include
#include
#include
#include
//This code is written by Itstusing namespace std;#define int long longinline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a;}const int MAXN = 2e5 + 7 , MOD = 998244353;int dp[MAXN][2] , arr[MAXN] , N , K;inline int poww(int a , int b){ int times = 1; while(b){ if(b & 1) times = times * a % MOD; a = a * a % MOD; b >>= 1; } return times;}signed main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout);#endif N = read(); K = read(); for(int i = 1 ; i <= N ; ++i) arr[i] = read(); vector < int > ind; ind.clear(); for(int i = 1 ; i <= N ; i += 2) if(arr[i] != -1) ind.push_back((i + 1) >> 1); int times1 , times2; if(!ind.empty()){ dp[0][0] = 1; int pos = 0; for(int i = 1 ; pos < ind.size() ; ++i) if(ind[pos] == i){ dp[i][0] = dp[i - 1][0]; if(++pos < ind.size() && arr[ind[pos] * 2 - 1] == arr[ind[pos - 1] * 2 - 1]) swap(dp[i][0] , dp[i][1]); } else{ if(i == 1){ dp[i][0] = K - 1; dp[i][1] = 1; } else{ dp[i][0] = (dp[i - 1][0] * (K - 2) + dp[i - 1][1] * (K - 1)) % MOD; dp[i][1] = dp[i - 1][0]; } } --pos; times1 = poww(K - 1 , (N + 1) / 2 - ind[pos]) * (dp[ind[pos]][0] + dp[ind[pos]][1]) % MOD; } else times1 = K * poww(K - 1 , (N + 1) / 2 - 1) % MOD; ind.clear(); for(int i = 2 ; i <= N ; i += 2) if(arr[i] != -1) ind.push_back(i >> 1); if(!ind.empty()){ memset(dp , 0 , sizeof(dp)); dp[0][0] = 1; int pos = 0; for(int i = 1 ; pos < ind.size() ; ++i) if(ind[pos] == i){ dp[i][0] = dp[i - 1][0]; if(++pos < ind.size() && arr[ind[pos] * 2] == arr[ind[pos - 1] * 2]) swap(dp[i][0] , dp[i][1]); } else{ if(i == 1){ dp[i][0] = K - 1; dp[i][1] = 1; } else{ dp[i][0] = (dp[i - 1][0] * (K - 2) + dp[i - 1][1] * (K - 1)) % MOD; dp[i][1] = dp[i - 1][0]; } } --pos; times2 = poww(K - 1 , N / 2 - ind[pos]) * (dp[ind[pos]][0] + dp[ind[pos]][1]) % MOD; } else times2 = K * poww(K - 1 , N / 2 - 1) % MOD; cout << times1 * times2 % MOD; return 0;}

如果你做过,这道题应该不难想到怎么做

对于题目中"\((x_1 , y_1) \in R , (x_1 , y_2) \in R , (x_2 , y_1) \in R , (x_2 , y_2) \neq R\)时将\((x_2,y_2)\)加入点集中"的操作,可以使用并查集描述:

并查集中存\(6 \times 10^5\)个点分别表示行和列,对于一个点\((x,y)\),将第\(x\)行和第\(y\)列在并查集中合并。注意到加入\((x_1 , y_1)(x_1 , y_2)(x_2 , y_1)\)之后,\(x_2\)行和\(y_2\)列就在同一个并查集里了,这就表示\((x_2,y_2)\)成为了点集\(R\)中的一个点。不难得到加完所有点后,答案是所有并查集包含的行数和列数的乘积之和。

注意到要删点,所以使用线段树分治+带撤销并查集做一下就可以了。

#include
#include
#include
#include
#include
#include
#include
//This code is written by Itstusing namespace std;#define int long longinline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a;}const int MAXN = 6e5 + 7;#define PII pair < int , int >#define st first#define nd secondmap < PII , int > mp;vector < PII > Edge[MAXN << 2];int fa[MAXN] , szX[MAXN] , szY[MAXN] , Q , ans;int find(int x){ return fa[x] == x ? x : find(fa[x]);}#define mid ((l + r) >> 1)#define lch (x << 1)#define rch (x << 1 | 1)void addEdge(int x , int l , int r , int L , int R , PII pos){ if(l >= L && r <= R){ Edge[x].push_back(pos); return; } if(mid >= L) addEdge(lch , l , mid , L , R , pos); if(mid < R) addEdge(rch , mid + 1 , r , L , R , pos);}void merge(int x , int y , stack < int > &stk){ x = find(x); y = find(y); if(x == y) return; ans -= szX[x] * szY[x] + szX[y] * szY[y]; if(szX[x] + szY[x] < szX[y] + szY[y]) swap(x , y); fa[y] = x; szY[x] += szY[y]; szX[x] += szX[y]; ans += szX[x] * szY[x]; stk.push(y);}void work(int x , int l , int r){ stack < int > stk; int lastans = ans; for(auto t : Edge[x]) merge(t.st , t.nd + 300000 , stk); if(l == r) printf("%lld " , ans); else{ work(lch , l , mid); work(rch , mid + 1 , r); } while(!stk.empty()){ int t = stk.top(); stk.pop(); int p = find(t); szX[p] -= szX[t]; szY[p] -= szY[t]; fa[t] = t; } ans = lastans;}signed main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout);#endif Q = read(); for(int i = 1 ; i <= Q ; ++i){ int a = read() , b = read(); PII t = PII(a , b); if(mp.find(t) == mp.end()) mp[t] = i; else{ addEdge(1 , 1 , Q , mp[t] , i - 1 , t); mp.erase(mp.find(t)); } } for(auto t : mp) addEdge(1 , 1 , Q , t.second , Q , t.first); for(int i = 1 ; i <= 3e5 ; ++i) szX[fa[i] = i] = 1; for(int i = 3e5 + 1 ; i <= 6e5 ; ++i) szY[fa[i] = i] = 1; work(1 , 1 , Q); return 0;}

考虑转移问题的优先级:我们要求\((u,v)(2 | u , 2|v)\)\((u,v)(2 \not\mid u , 2 \not\mid v)\)的总经过次数最小,其次总边权最小。

既然两棵树同构,可以把这两棵树拍扁到一起变成一棵树,那么我们需要在这一棵树上经过边数最小,那么经过的显然是两点之间的路径。

上面的问题可以在拍扁的树上树形DP+倍增处理:设\(f_{i,j,k=0/1,l=0/1}\)表示:设从\(i\)开始跳\(2^j\)次方步到达的点为\(x\),那么原图中\((2 \times i + k , 2 \times x + l)\)的最短路是多少。注意上面的\(i,x\)指的是在拍扁的树上的编号。转移类似于矩阵乘法。

注意到转移优先级之后有可能答案不优,因为可能存在某些情况会绕一段路到另一棵树上,边权总和比直接走的边权要小。如果能将所有\((2i - 1 , 2i)\)的边权变为\(2i-1\)\(2i\)之间的最短路长度,那么上面的情况就会覆盖\((2i - 1 , 2i)\)的边权,在DP过程中就不需要考虑了。

所以最后的问题是如何求出所有\(2i-1\)\(2i\)的最短路。有一个很精妙的SSSP做法:建\(N+1\)个点编号为\(0\)\(N\),连边\((0,i,w_{2i-1,2i})\),对于树上存在的边\((i,j,w_1,w_2)\)在图上连\((i,j,w_1+w_2)\),跑Dijkstra,得到\(0\)\(i\)的最短路长度就是\(2i-1\)\(2i\)的最短路长度。

#include
#include
#include
#include
//This code is written by Itstusing namespace std;#define int long longinline int read(){ int a = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return a;}const int MAXN = 3e5 + 7;int N , Q;struct Edge{int end , upEd , w;};inline void addEd(Edge *Ed , int *head , int &cntEd , int a , int b , int c , bool f = 0){ Ed[++cntEd] = (Edge){b , head[a] , c}; head[a] = cntEd; if(f){ Ed[++cntEd] = (Edge){a , head[b] , c}; head[b] = cntEd; }}namespace SSSP{#define PII pair < int , int >#define st first#define nd second Edge Ed[MAXN << 2]; int head[MAXN] , dis[MAXN]; int cntEd; priority_queue < PII > q; void work(){ memset(dis , 0x7f , sizeof(dis)); dis[0] = 0; q.push(PII(0 , 0)); while(!q.empty()){ PII t = q.top(); q.pop(); if(dis[t.nd] != -t.st) continue; for(int i = head[t.nd] ; i ; i = Ed[i].upEd) if(dis[t.nd] + Ed[i].w < dis[Ed[i].end]) q.push(PII(-(dis[Ed[i].end] = dis[t.nd] + Ed[i].w) , Ed[i].end)); } }}namespace Tree{ Edge Ed[MAXN << 1]; struct matrix{ int a[2][2]; matrix(){memset(a , 0x3f , sizeof(a));} int* operator [](int x){return a[x];} matrix operator *(matrix b){ matrix c; for(int i = 0 ; i < 2 ; ++i) for(int j = 0 ; j < 2 ; ++j) for(int k = 0 ; k < 2 ; ++k) c[j][k] = min(c[j][k] , a[j][i] + b[i][k]); return c; } }dis[MAXN][20] , tmp , initial , toX , toY; int head[MAXN] , dep[MAXN] , fa[MAXN][20] , val2[MAXN << 1] , cntEd; void dfs(int x , int p , matrix val){ dis[x][0] = val; fa[x][0] = p; for(int i = 1 ; fa[x][i - 1] ; ++i){ fa[x][i] = fa[fa[x][i - 1]][i - 1]; dis[x][i] = dis[x][i - 1] * dis[fa[x][i - 1]][i - 1]; } dep[x] = dep[p] + 1; for(int i = head[x] ; i ; i = Ed[i].upEd) if(Ed[i].end != p){ tmp[1][0] = min(Ed[i].w + SSSP::dis[Ed[i].end] , val2[i] + SSSP::dis[x]); tmp[0][1] = min(val2[i] + SSSP::dis[Ed[i].end] , Ed[i].w + SSSP::dis[x]); tmp[0][0] = min(Ed[i].w , tmp[0][1] + SSSP::dis[x]); tmp[1][1] = min(val2[i] , tmp[1][0] + SSSP::dis[x]); dfs(Ed[i].end , x , tmp); } } void init(){ memset(initial.a , 0 , sizeof(initial)); dfs(1 , 0 , initial); } int jump(int x , int y , bool f1 , bool f2){ if(dep[x] < dep[y]){ x ^= y ^= x ^= y; swap(f1 , f2); } toX = initial; toY = initial; toX[0][1] = toX[1][0] = SSSP::dis[x]; toY[0][1] = toY[1][0] = SSSP::dis[y]; for(int i = 18 ; i >= 0 ; --i) if(dep[x] - (1 << i) >= dep[y]){ toX = toX * dis[x][i]; x = fa[x][i]; } if(x == y) return toX[f1][f2]; for(int i = 18 ; i >= 0 ; --i) if(fa[x][i] != fa[y][i]){ toX = toX * dis[x][i]; toY = toY * dis[y][i]; x = fa[x][i]; y = fa[y][i]; } toX = toX * dis[x][0]; toY = toY * dis[y][0]; return min(toX[f1][0] + toY[f2][0] , toX[f1][1] + toY[f2][1]); } void work(int x , int y){ bool f1 = !(x & 1) , f2 = !(y & 1); x = (x + 1) >> 1; y = (y + 1) >> 1; printf("%lld\n" , jump(x , y , f1 , f2)); }}signed main(){#ifndef ONLINE_JUDGE freopen("in","r",stdin); freopen("out","w",stdout);#endif N = read(); for(int i = 1 ; i <= N ; ++i) addEd(SSSP::Ed , SSSP::head , SSSP::cntEd , 0 , i , read()); for(int i = 1 ; i < N ; ++i){ int a = read() , b = read() , w1 = read() , w2 = read(); addEd(SSSP::Ed , SSSP::head , SSSP::cntEd , a , b , w1 + w2 , 1); addEd(Tree::Ed , Tree::head , Tree::cntEd , a , b , w1 , 1); Tree::val2[Tree::cntEd] = Tree::val2[Tree::cntEd - 1] = w2; } SSSP::work(); Tree::init(); for(int Q = read() ; Q ; --Q) Tree::work(read() , read()); return 0;}

转载于:https://www.cnblogs.com/Itst/p/10585302.html

你可能感兴趣的文章
【计算机视觉】期刊整理
查看>>
【Linux开发】linux中关于dma_alloc_coherent的用法
查看>>
Sublime 输入中文显示方框问号乱码
查看>>
bootstrap-datepicker宽度高度自适应
查看>>
字符串函数
查看>>
带返回值的存储过程
查看>>
表格测试
查看>>
Android 命名规范 (提高代码可以读性) 转
查看>>
移动设备尺寸规范汇总(转)
查看>>
Oracle 创建用户,表空间
查看>>
map set区别
查看>>
Mysql
查看>>
面向对象-面向对象和面向过程的区别
查看>>
数组Array的一些方法
查看>>
window10设置文件的默认打开方式
查看>>
SQLiteOpenHelper 升级onUpgrade 的调用问题
查看>>
android Firebase中配置 Crashlytics
查看>>
典型的阻容降压电路
查看>>
SQL数据库数据类型详解
查看>>
MVC 服务器文件下载
查看>>