退役选手复健
Free ticket
题解:
floyd之后直接找最大值。
/**
https://www.codechef.com/INOIPRAC/problems/INOI1402
*/
#include
#define ll long long
using namespace std;
const int INF = 1e9 + 4;
const int MAXN = 3e2 + 4;
int mp[MAXN][MAXN];
int N,M;
int w33ha() {
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
if(i != j) {
mp[i][j] = INF;
} else
mp[i][j] = 0;
}
}
for(int i=1; i<=M; i++) {
int u, v, w;
scanf("%d%d%d",&u,&v,&w);
mp[u][v] = min(mp[u][v], w);
mp[v][u] = min(mp[v][u], w);
}
for(int k=1; k<=N; k++)
for(int i=1; i<=N; i++)
for(int j=1; j<=N; j++)
mp[i][j] = min(mp[i][j], mp[i][k]+mp[k][j]);
vector<int> o;
for(int i=1; i<=N; i++)
for(int j=1; j<=N; j++)
if(i!=j) {
o.push_back(mp[i][j]);
}
sort(o.begin(), o.end(), greater<int>());
printf("%d\n", *o.begin());
return 0;
}
int main() {
while(scanf("%d%d",&N, &M)!=EOF)
w33ha();
return 0;
}
Triathlon
题解:
后面两个比赛都可以同时进行,所以时间可以合起来算。
每个人的结束时间就是(在之前的人使用电脑的总时间+当前这个人进行其他比赛时间)
要让最大结束时间最小,那么考虑贪心,A.c,B.c是用电脑的时间,A.o,B.o是其他比赛的时间。那么A要在前面就要满足
max(A.c + A.o, A.c + B.c + B.o) < max(A.c + B.c + A.o, B.c + B.o)
即可。
/**
https://www.codechef.com/INOIPRAC/problems/INOI1201
*/
#include
#define ll long long
using namespace std;
const int MAXN = 2e5 + 4;
struct Ds {
int c, o;
} a[MAXN];
inline bool dex(Ds A, Ds B) {
return max(A.c + A.o, A.c + B.c + B.o) < max(A.c + B.c + A.o, B.c + B.o);
}
int N;
int w33ha() {
for(int i=1; i<=N; i++) {
int x, y, z;
scanf("%d%d%d",&x,&y,&z);
a[i].c = x ;
a[i].o = y + z;
}
sort(a + 1, a + N + 1, dex);
ll ans = 0, rest = 0;
for(int i=1; i<=N; i++) {
ans = max(ans, rest + a[i].c + a[i].o);
rest += a[i].c;
}
printf("%lld\n",ans);
return 0;
}
int main() {
while(scanf("%d",&N)!=EOF)
w33ha();
return 0;
}
Calvins Game
题解:DAG最长路,不过权值在点上。
from1: 从1开始往后走到i的点的权值和。
fromk: 从k开始往后走到i的点的最大权值和。
(包含起点终点的)
然后我们就是求最大的
from1[i] - a[i] + fromk[i] - a[k]
还是比较简单的图论题
#include
#define ll long long
using namespace std;
const ll INF = 1e14;
const int MAXN = 1e6 + 4;
ll dis[MAXN];
ll from1[MAXN], fromk[MAXN];
int n,k,a[MAXN];
vector<int>G[MAXN];
short inq[MAXN];
void add(int x,int y) {
G[x].push_back(y);
}
queue<int>q;
void spfa(int st) {
for(int i=1; i<=n; i++)
dis[i] = -INF;
dis[st] = a[st];
q.push(st);
inq[st] = 1;
while(!q.empty()) {
int x = q.front();
q.pop();
for(int i=0; i<G[x].size(); i++) {
int to = G[x][i];
if(dis[to] < dis[x] + a[to]) {
dis[to] = dis[x] + a[to];
if(!inq[to]) {
inq[to] = 1;
q.push(to);
}
}
}
inq[x] = 0;
}
}
int w33ha() {
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
for(int i=1; i<=n; i++) {
if(i<n)
add(i,i+1);
if(i+1<n)
add(i,i+2);
}
spfa(1);
memcpy(from1, dis, (n+1)*sizeof(ll));
spfa(k);
memcpy(fromk, dis, (n+1)*sizeof(ll));
ll ans = from1[k] - a[k];
for(int i=k+1; i<=n; i++) {
ans = max(ans, from1[i] - a[i] + fromk[i] - a[k]);
}
printf("%lld\n",ans);
return 0;
}
int main() {
while(scanf("%d%d",&n,&k)!=EOF)
w33ha();
return 0;
}
Sequence Land
题解:并查集+set判断交集
/**
https://www.codechef.com/INOIPRAC/problems/INOI1302
**/
#include
#define ll long long
using namespace std;
const ll INF = 1e14;
const int MAXN = 304;
int n,k;
set<int> o[MAXN];
int sz[MAXN], f[MAXN];
int Find(int x) {
return f[x] == x ? x : f[x] = Find(f[x]);
}
int Count(set<int>& A, set<int>& B) {
int cnt = 0;
for(auto x:A) {
if(B.find(x) != B.end()) {
++ cnt;
}
}
return cnt;
}
int w33ha() {
for(int i=1; i<=n; i++) {
f[i] = i;
sz[i] = 1;
}
for(int i=1; i<=n; i++) {
o[i].clear();
int cnt,x;
scanf("%d",&cnt);
for(int j=1; j<=cnt; j++) {
scanf("%d",&x);
o[i].insert(x);
}
for(int j=1; j<i; j++) {
if(Count(o[i], o[j]) >= k) {
int x = Find(i), y = Find(j);
if(x != y) {
f[y] = x;
sz[x] += sz[y];
}
}
}
}
printf("%d\n",sz[Find(1)]);
return 0;
}
int main() {
while(scanf("%d%d",&n,&k)!=EOF)
w33ha();
return 0;
}
Wealth Disparity
题解:max(子树根节点值 - 子树最小值) ,dfs即可
/**
https://www.codechef.com/INOIPRAC/problems/INOI1601
**/
#include
#define ll long long
using namespace std;
const int INF = 2e9;
const int MAXN = 1e5 + 4;
vector<int> G[MAXN];
int n, st, ans;
int a[MAXN], mint[MAXN];
void dfs(int x) {
mint[x] = INF;
for(int i=0; i<G[x].size(); i++) {
int to = G[x][i];
dfs(to);
mint[x] = min(mint[x], mint[to]);
}
ans = max(a[x] - mint[x], ans);
mint[x] = min(mint[x], a[x]);
}
int w33ha() {
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
st = -1;
for(int i=1; i<=n; i++) {
int x;
scanf("%d",&x);
if(x == -1)
st = i;
else {
G[x].push_back(i);
}
}
ans = -INF;
dfs(st);
printf("%d\n",ans);
return 0;
}
int main() {
while(scanf("%d",&n)!=EOF)
w33ha();
return 0;
}
Table Sum
题解:
用线段树来模拟循环位移即可
/**
https://www.codechef.com/INOIPRAC/problems/INOI1202
**/
#include
#define ll long long
using namespace std;
const int MAXN = 2e5 + 4;
struct tree {
int l,r,w,tag;
} tr[MAXN << 2];
int v[MAXN], n ;
void build(int k, int l, int r) {
tr[k].l = l, tr[k].r = r;
if(l == r) {
tr[k].tag = 0;
tr[k].w = v[l];
return ;
}
int mid = (l+r)>>1;
build(k<<1, l, mid);
build(k<<1|1, mid+1, r);
tr[k].w = max(tr[k<<1].w, tr[k<<1|1].w);
}
void pushdown(int k) {
int gl = (k<<1), gr = (k<<1|1), val = tr[k].tag;
tr[gl].tag += val;
tr[gr].tag += val;
tr[k].tag = 0;
tr[gl].w += val;
tr[gr].w += val;
}
void change(int k,int a,int b,int x) {
int l = tr[k].l, r = tr[k].r;
if(l == a && r == b) {
tr[k].tag += x;
tr[k].w += x;
return ;
}
pushdown(k);
int mid = (l+r)>>1;
if(b <= mid)
change(k<<1,a,b,x);
else if(a > mid)
change(k<<1|1,a,b,x);
else {
change(k<<1,a,mid,x);
change(k<<1|1,mid+1,b,x);
}
tr[k].w = max(tr[k<<1].w, tr[k<<1|1].w);
}
vector<int> ans;
int w33ha() {
ans.clear();
for(int i=1; i<=n; i++) {
scanf("%d",&v[i]);
v[i] += i;
}
build(1, 1, n);
ans.push_back(tr[1].w);
for(int i=1; i<n; i++) {
change(1,n-i+1,n-i+1,-n+1);
change(1,1,n-i,1);
if(n-i+2<=n)
change(1,n-i+2,n,1);
ans.push_back(tr[1].w);
}
for(int i=0; i<ans.size(); i++) {
if(i == (int)ans.size()-1)
printf("%d\n",ans[i]);
else
printf("%d ",ans[i]);
}
return 0;
}
int main() {
while(scanf("%d",&n) != EOF)
w33ha();
return 0;
}
Special Sums
题解:3种情况
a[i] (i==j)
a[i] + a[j] - sumbFront[i] + sumbFront[j-1] (i<j)
a[i] + a[j] - sumbFront[i-1] + sumbBack[j+1] (i>j)
前缀/后缀和之后,可以枚举i,j的话可以用后缀最大值来代替
/**
https://www.codechef.com/INOIPRAC/problems/INOI1501
*/
#include
#define ll long long
using namespace std;
const int MAXN = 1e6 + 4;
const ll INF = 1e18;
ll bBack[MAXN] , bFront[MAXN];
ll maxBack[MAXN] , maxFront[MAXN];
ll a[MAXN],b[MAXN];
int n;
int w33ha(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
bFront[0] = 0;
bBack[n+1] = 0;
for(int i=1,j=n;i<=n&&j>=1;i++,j--){
bFront[i] = bFront[i-1] + b[i];
bBack[j] = bBack[j+1] + b[j];
}
maxFront[n+1] = -INF;
maxBack[n+1] = -INF;
for(int i=n;i>=1;i--){
maxFront[i] = max(maxFront[i+1] , a[i] + bFront[i-1]);
maxBack[i] = max(maxBack[i+1] , a[i] + bBack[i+1]);
}
ll ans = a[1];
for(int i=1;i<=n;i++){
ans = max(ans , a[i]);
}
for(int i=1;i<n;i++){
ll p1 = a[i] - bFront[i] + maxFront[i+1];
ll p2 = a[i] + bFront[i-1] + maxBack[i+1];
//cout << i <<" : " << p1 <<" " << p2 << endl;
ans = max(ans , max(p1,p2));
}
printf("%lld\n",ans);
return 0;
}
int main(){
w33ha();
return 0;
}
Highway Bypass
题解,棋盘dp , f[i][j][k][0/1]
表示从起点到(i,j),已经按照当前方向行走了k步,当前方向是 横着0 / 竖着1 的方案数
注意边界条件。
/**
https://www.codechef.com/INOIPRAC/problems/INOI1401
*/
#include
#define ll long long
using namespace std;
const int mod = 20011;
const int MAXN = 304;
int f[MAXN][MAXN][MAXN][2];
int r,c,d;
int mp[MAXN][MAXN];
int main(){
memset(f,0,sizeof(f));
scanf("%d%d%d",&r,&c,&d);
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
scanf("%d",&mp[i][j]);
if(r == 1&& c == 1) return puts("1"),0;
for(int i=1;i<=r;i++){
for(int j=1;j<=c;j++){
if(mp[i][j] == 0) continue;
if(i==1&&j==1) continue;
if(i==1&&j==2){
f[i][j][1][0] = 1; //cout << i << " _ " << j << " 0"<< endl;
continue;
}
if(i==2&&j==1){
f[i][j][1][1] = 1; //cout << i << " _ " << j << " 1" << endl;
continue;
}
f[i][j][1][1] = 0;
f[i][j][1][0] = 0;
//cout << i << " "<< j << endl;
for(int k=1;k<=d;k++){
f[i][j][1][0] += f[i][j-1][k][1];
f[i][j][1][1] += f[i-1][j][k][0];
f[i][j][1][0] %= mod;
f[i][j][1][1] %= mod;
//cout << f[i][j][1][0] <<" "<< f[i][j][1][1] << endl;
}
for(int k=2;k<=d;k++){
f[i][j][k][0] += f[i][j-1][k-1][0];
f[i][j][k][0] %= mod;
f[i][j][k][1] += f[i-1][j][k-1][1];
f[i][j][k][1] %= mod;
}
}
}
int ans = 0;
for(int i=1;i<=d;i++){
for(int t=0;t<2;t++){
ans = (f[r][c][i][t]+ans) % mod;
}
}
printf("%d\n",ans);
return 0;
}
Periodic Strings
题解:枚举n的因数,计算每个因数对答案的贡献。我们可以发现,对于长度n,答案是 t [ n ] = 2 n − ∑ n m o d i = 0 t [ i ] t[n] = 2^n - \sum_{n \mod i \ = \ 0} t[i] t[n]=2n−nmodi = 0∑t[i] ,递推计算 t [ i ] t[i] t[i]即可
r e s [ 1 ] = 2 res[1] = 2 res[1]=2
/**
https://www.codechef.com/INOIPRAC/problems/INOI1502
*/
#include
#define ll long long
using namespace std;
const int MAXN = 150004;
ll n,m;
vector<ll> vec;
ll t[MAXN];
ll fp(ll x , ll y){
if(y == 0) return 1LL % m;
ll temp = fp(x,y/2);
if(y & 1){
return temp * temp % m * x % m;
}
else {
return temp * temp % m;
}
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=2;i<=sqrt(n);i++){
if(n % i == 0) {
vec.push_back(i);
if(n/i!=i)vec.push_back(n/i);
}
}
if(n == 1){
printf("%lld\n",2LL % m);
return 0;
}
sort(vec.begin() , vec.end());
if(vec.size() == 0){
printf("%lld\n",(fp(2LL,n) - (2%m) + m) % m);
return 0;
}
t[vec[0]] = (fp(2LL,vec[0]) - (2%m) + m) % m;
vec.push_back(n);
for(int i=1;i<vec.size();i++){
ll x = vec[i];
ll sum = (fp(2LL,x) - (2%m) + m) % m;
for(int i=2;i<=sqrt(x);i++){
if(x % i == 0){
sum -= t[i];
sum = ((sum % m) + m) %m;
if(x / i != i) sum -= t[x/i];
sum = ((sum % m) + m) %m;
}
}
t[x] = sum;
}
printf("%lld\n",t[n]);
return 0;
}
1 Road Trips and Museums - INOI 2018
每次做INOI都有一种大脑被弓虽女干的感觉(不对劲
考虑每个联通块就行,dfs求出每个联通块内部的博物馆数量,把联通块排序之后,博物馆数量最多的和最少的联通块交替取即可。
/**
https://www.codechef.com/INOIPRAC/problems/ROADTRIP
*/
#include
#define ll long long
using namespace std;
const int MAXN = 1e6 + 4;
vector<int> G[MAXN];
vector<ll> ans;
int n,m,k,museum[MAXN];
short vis[MAXN];
ll sum;
void dfs(int x){
vis[x] = 1;
sum += museum[x];
for(int i=0;i<G[x].size();i++){
if(!vis[G[x][i]])dfs(G[x][i]);
}
}
int w33ha(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;i++)scanf("%d",&museum[i]);
for(int i=1;i<=n;i++){
if(!vis[i]){
sum = 0;
dfs(i);
ans.push_back(sum);
}
}
sort(ans.begin(),ans.end(),greater<ll>());
int l=0,r=(int)ans.size()-1;
if(ans.size()<k){
puts("-1");
} else {
sum = 0;
for(int i=0;i<k;i++){
if(i%2==0)sum += ans[l++];
else sum += ans[r--];
}
printf("%lld\n",sum);
}
ans.clear();
for(int i=1;i<=n;i++) {
G[i].clear();
vis[i] = 0;
}
return 0;
}
int main(){
int T;scanf("%d",&T);
while(T --) w33ha();
return 0;
}
Training - INOI 2017
DP题
f [ i ] [ j ] f[i][j] f[i][j]表示一共升级了 i i i次,当前在 j j j号点去打道馆,获得的最多的经验。 g [ i ] [ j ] g[i][j] g[i][j]一共升级了 i i i次,当前在 j j j号点,继续升级,获得的最多经验。 s [ i ] s[i] s[i]是升级 i i i次之后的每次比赛的经验值, s e [ i ] se[i] se[i]是 E [ i ] E[i] E[i]的前缀和。
f [ i ] [ j ] = m a x ( g [ i − 1 ] [ k ] + s [ i ] ∗ ( s e [ j ] − s e [ k ] ) , f [ i ] [ j ] ) f[i][j] = max(g[i-1][k]+s[i]*(se[j] - se[k]),f[i][j]) f[i][j]=max(g[i−1][k]+s[i]∗(se[j]−se[k]),f[i][j])
g [ i ] [ j ] = m a x ( g [ i − 1 ] [ k ] + s [ i ] ∗ ( s e [ j − 1 ] − s e [ k ] ) , g [ i ] [ j ] ) g[i][j] = max(g[i-1][k]+s[i]*(se[j-1] - se[k]),g[i][j]) g[i][j]=max(g[i−1][k]+s[i]∗(se[j−1]−se[k]),g[i][j])
对于每个 f [ i ] [ j ] f[i][j] f[i][j], g [ i ] [ j ] g[i][j] g[i][j]来说,其实 g [ i − 1 ] [ k ] − s [ i ] ∗ s e [ k ] g[i-1][k]-s[i]*se[k] g[i−1][k]−s[i]∗se[k]都可以用前缀最大值处理。然后在枚举j的时候计算 s [ i ] ∗ s e [ j ] s[i]*se[j] s[i]∗se[j]和 s [ i ] ∗ s e [ j − 1 ] s[i]*se[j-1] s[i]∗se[j−1]即可。
/**
https://www.codechef.com/INOIPRAC/problems/TINOI17B
*/
#include
#define ll long long
using namespace std;
const ll INF = 1e16;
const int MAXN = 5e3 + 4;
ll cubify(ll x){
ll v = 0;
while(x){
v += x % 10;
x /= 10;
}
return v * v * v;
}
ll s[MAXN] , se[MAXN];
ll f[2][MAXN] , g[2][MAXN];
int n;
int main(){
scanf("%d%lld",&n,&s[0]);
for(int i=1;i<=n;i++){
ll x;
scanf("%lld",&x);
se[i] = se[i-1] + x;
s[i] = s[i-1] + cubify(s[i-1]);
}
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
ll ans = 0;
for(int i=0;i<=n;i++){
int t = (i&1);
memset(f[t],0,sizeof(f[t]));
memset(g[t],0,sizeof(g[t]));
ll maxn = - s[i] * se[i];
for(int j=i+1;j<=n;j++){
f[t][j] = maxn + s[i] * se[j];
g[t][j] = maxn + s[i] * se[j-1];
maxn = max(maxn , g[t^1][j] - s[i] * se[j]);
ans = max(ans , f[t][j]);
}
}
printf("%lld\n",ans);
return 0;
}
Brackets
区间dp,f[i][j]表示区间[i,j]的括号区间的最大和,直接硬做即可
/**
https://www.codechef.com/INOIPRAC/problems/INOI1602
*/
#include
#define ll long long
using namespace std;
const int MAXN = 704;
int K , n;
int v[MAXN] , b[MAXN];
ll f[MAXN][MAXN];
int w33ha(){
memset(f,0,sizeof(f));
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)scanf("%d",&v[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int l=2;l<=n;l++){
for(int i=1;i+l-1<=n;i++){
int j = i+l-1;
f[i][j] = max(f[i][j-1],f[i+1][j]);
if(b[i]+K == b[j]){
//cout << i << " " << j << endl;
f[i][j] = max(v[i]+v[j]+f[i+1][j-1],f[i][j]);
}
for(int k=i+1;k<j;k++){
f[i][j] = max(f[i][j] , f[i][k] + f[k+1][j]);
}
}
}
ll ans = 0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
ans = max(ans , f[i][j]);
printf("%lld\n",ans);
return 0;
}
/**
6 3
4 5 -2 1 1 6
1 3 4 2 5 6
*/
int main(){
w33ha();
return 0;
}