标签:
马克飞象莫名其妙无法同步到印象笔记,现在这里保存一下吧。
@(ACM集训)
无向图,给定边及边权重,任意两点之间都有一条唯一的道路,道路上每个点只能出现一次。给定询问,求询问的结点之间的距离。
路上每个点只能出现一次,可以转化成有根树,问题也即为求最近公共祖先问题~~ 这里每条边加上了距离,求出LCA后,用
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define fuck cout<<"fuck"<<endl;
const int maxn = 40005, maxq = 205;
typedef pair<int, int>p;
vector<p>G[maxn];
#define fi first
#define se second
int pa[maxn];
bool vis[maxn];
bool in[maxn];
int ance[maxn];
int dist[maxn];
int n, tot;
int head[maxn];
int ans[maxn];
struct Query{int to, next, index;};
Query query[maxq * 2];
void add_query(int u, int v, int index)
{
query[tot].to = v;
query[tot].index = index;
query[tot].next = head[u];
head[u] = tot++;
query[tot].to = u;
query[tot].index = index;
query[tot].next = head[v];
head[v] = tot++;
}
int _find(int x)
{
if(pa[x] != x) return pa[x] = _find(pa[x]);
return x;
}
void unite(int x, int y)
{
int rx = _find(x), ry = _find(y);
if(rx == ry) return;
pa[rx] = ry;
}
void init()
{
tot = 0;
for(int i = 1; i <= n; i++){
G[i].clear();
pa[i] = i;
}
mem(ance, 0);
mem(vis, false);
mem(head, -1);
mem(dist, 0);
mem(in, false);
mem(ans, 0);
}
void LCA(int u)
{
ance[u] = u;
vis[u] = true;
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i].fi;
if(vis[v]) continue;
dist[v] = dist[u] + G[u][i].se;
LCA(v);
unite(u, v);
ance[_find(u)] = u;
}
for(int i = head[u]; i != -1; i = query[i].next){
int v = query[i].to;
if(vis[v]) ans[query[i].index] = dist[u] + dist[v] - 2 * dist[ance[_find(u)]];
}
}
int main(void)
{
int u, v, k;
int a, b, c;
int Q;
int T;scanf("%d", &T);
while(T--){
init();
scanf("%d%d", &n, &Q);
for(int i = 0; i < n - 1; i++){
scanf("%d%d%d",&a, &b, &c);
G[a].push_back(p(b, c));
G[b].push_back(p(a, c));
}
for(int i = 0; i < Q; i++){
scanf("%d%d",&u,&v);
add_query(u, v, i);
}
dist[1] = 0;
LCA(1);
for(int i = 0; i <Q; i++){
printf("%d\n", ans[i]);
}
}
return 0;
}
经典过河问题,一个船只能运两个人,每个人过河时间不同,船来回往返使得
小学奥数题的感觉。。
决策如下:
1.
2.
3.
- 最小的来回往返,一次送最慢,回来,再送次慢。
- 最小和次小的一起送(要保证最慢的和次慢的过去以后还有人能把船运回来),最小的和次小的先一起过去,然后最小的回来,然后最慢的和次慢的一起过去,次小的回来。
取两种方法中的最小值即可。依次循环按上述策略处理。
/*************************************************************************
> File Name:
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
typedef pair<int, int>p;
typedef long long ll;
const int maxn = 1e3 + 5;
int a[maxn];
int main (void)
{
int T;cin>>T;
while(T--){
int n;cin>>n;
for(int i = 0; i < n; i++) cin>>a[i];
sort(a, a + n);
int ans = 0;
while(n){
if(n == 1){
ans += a[0];
break;
} else if(n == 2){
ans += a[1];
break;
}else if(n == 3){
ans += a[0] + a[1] + a[2];
break;
}else{
int t1 = a[0] * 2 + a[n - 1] + a[n - 2];
int t2 = a[1] * 2 + a[0] + a[n - 1];
n -= 2;
ans += min(t1, t2);
}
}
cout<<ans<<endl;
}
return 0;
}
给定
可以将问题看成,两只蚂蚁相遇后交换速度和方向,那么本质就相当于相遇后直接穿过对方,继续原方向行走,而由于两只蚂蚁相遇后直接掉头,所以最后 所有蚂蚁的相对顺序保持不变,这样我们算出所有终态,排个序,按照对应初始位置的顺序输出即可。注意判断是否掉下去的优先级要比位置是否相同的高!
/*************************************************************************
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x
#define pl(x) cout<<#x<<":"<<x<<endl
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%I64d", &(n))
typedef long long ll;
const int maxn = 1e4 + 5, oo = 0x3f3f3f3f;
struct NODE{int x; int dir; int id;};
NODE node[maxn], nnode[maxn];
map<int, int>MAP;
bool cmp(NODE a, NODE b)
{
return a.x < b.x;
}
int main (void)
{
int N;sa(N);
for(int kas = 1; kas <= N; kas++){
int L, T, n;sa(L);sa(T);sa(n);
int x;
for(int i = 1; i <= n; i++){
sa(x);getchar();
if(getchar() == ‘L‘){
node[i] = (NODE){x, 1, i};
nnode[i] = (NODE){x - T, 1, 0};
}else{
node[i] = (NODE){x, 2, i};
nnode[i] = (NODE){x + T, 2, 0};
}
}
sort(node + 1, node + n + 1, cmp);
for(int i = 1; i <= n; i++){
MAP[node[i].id] = i;
}
sort(nnode + 1, nnode + n + 1, cmp);
for(int i = 1; i <= n; i++){
if(nnode[i].x < 0 || nnode[i].x > L)
nnode[i].dir = -1;
else if(i < n && nnode[i].x == nnode[i + 1].x){
nnode[i].dir = 0;
nnode[i + 1].dir = 0;
}
}
printf("Case #%d:\n", kas);
for(int j = 1; j <= n; j++){
int i = MAP[j];
if(nnode[i].dir == -1) {
cout<<"Fell off"<<endl;
continue;
}
cout<<nnode[i].x<<‘ ‘;
if(nnode[i].dir == 1){
cout<<‘L‘<<endl;
}else if(nnode[i].dir == 2){
cout<<‘R‘<<endl;
}else if(nnode[i].dir == 0){
cout<<"Turning"<<endl;
}
}
cout<<endl;
/*for(int i = 1; i <= n; i++){
cout<<nnode[i].id<<‘ ‘<<nnode[i].x<<‘ ‘<<nnode[i].dir<<endl;
}*/
}
return 0;
}
给定01序列,翻转一位其左右相邻两位也会被翻转,问使序列全部变为0的最小翻转次数。
简单线性排列,由于后面是否翻转由前面的决定,所以我们枚举第一个元素的状态,然后从头扫一遍即可。
/*************************************************************************
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x
#define pl(x) cout<<#x<<":"<<x<<endl
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%I64d", &(n))
typedef long long ll;
const int maxn = 20 + 5, oo = 0x3f3f3f3f;
int f[maxn], a[maxn];
int main (void)
{
for(int i = 0; i < 20; i++){
cin>>a[i];
}
memset(f, 0, sizeof(f));
int cnt = 0;
for(int i = 1; i < 20; i++){
if((a[i - 1] + f[i - 1]) & 1){
cnt++;
f[i]++;
f[i + 1]++;
}
}
memset(f, 0, sizeof(f));
int cnt2 = 1;
f[0] = f[1] = 1;
for(int i = 1; i < 20; i++){
if((a[i - 1] + f[i - 1]) & 1){
cnt2++;
f[i]++;
f[i + 1]++;
}
}
cout<<min(cnt, cnt2)<<endl;
return 0;
}
有
一个块的转动会影响其他块的状态,这里不是简单的线性排列,不能只踩黑块。
首先根据字典序,我们可以对第一排从
字典序,因为他说了是把整个排列当做字符串的字典序,所以肯定是越前面的越小越好,而且从第一个例子中也能看出来。
#include<iostream>
#include<cstring>
using namespace std;
#define mem(s,a) memset(s,a,sizeof(s));
int m, n;
const int maxn = 25, INF = 0x3fffffff;
int x[5]={-1,0,0,0,1};
int y[5] = {0,1,0,-1,0};
int s[maxn][maxn], a[maxn][maxn], r[maxn][maxn], ans[maxn][maxn];
int cal()
{
int cnt = 0;
for(int i = 2; i <= m; i++){
for(int j = 1; j <= n; j++){
if((a[i-1][j] + r[i-1][j]) % 2 == 1) {
cnt++;
s[i][j] = 1;
}
for(int k = 0; k < 5; k++){
r[i + x[k]][j + y[k]] += s[i][j];
}
}
}
for(int i =1; i <= n; i++){
if((r[m][i] + a[m][i]) % 2 == 1) return -1;
}
return cnt;
}
int main (void)
{
cin>>m>>n;
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
cin>>a[i][j];
}
}
int res = INF;
for(int i = 0; i <1<<n; i++){
mem(s,0);mem(r,0);
int t = 0;
for(int j =n; j >= 1; j--){
s[1][j] = i>>j&1;
for(int k = 0; k < 5; k++){
r[1 + x[k]][j + y[k]] += s[1][j];
}
t += s[1][j];
}
int tm = cal();
if(tm> = 0 && t + tm < res){
res = t + tm;
memcpy(ans,s,sizeof(s));
}
}
if(res == INF) {
cout<<"IMPOSSIBLE"<<endl;
return 0;
}
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
cout<<ans[i][j];
if(j != n) cout<<‘ ‘;
else cout<<endl;
}
}
return 0;
}
给定
最少,连续->双指针
挑战例题,双指针入门题
#include<cstdio>
#include<cstring>
#include<set>
#include<map>
#include<iostream>
using namespace std;
const int maxn = 1e6 + 5;
#define sa(n) scanf("%d", &n)
int a[maxn];
map<int, int>cnt;
int main (void)
{
int P;sa(P);
set<int>s;
for(int i = 0; i < P; i++){
sa(a[i]);
s.insert(a[i]);
}
int n = s.size();
int sz = 0;
int r;
for(r = 0; r < P; r++){
if(!cnt[a[r]]) sz++;
cnt[a[r]]++;
if(sz == n) break;
}
int l = 0;
int ans = r - l + 1;
while(r < P){
if(cnt[a[l]] == 1){
while(r < P && a[r] != a[l]) {
r++;
cnt[a[r]]++;
}
if(r == P) break;
}
cnt[a[l]]--;
ans = min(ans, r - l);
l++;
}
cout<<ans<<endl;
return 0;
}
给定序列及target,求一个子序列使得该序列和的绝对值最接近target。
挑战上的习题
求子区间和问题,先预处理个前缀和。
前缀和有正有负且不单调。因为题目中说的是区间和的绝对值最接近target,所以我们将前缀和排个序,这样保证求出的值为正即为相应区间求出绝对值之后的结果,整个序列单调,这样舍和取的条件非常明确,直接在上面二指针以获取值最接近target的即可。
观察题目中的性质区间和的绝对值,巧妙的化简问题。
最初用的
还有注意结果非空,即
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn = 1e5 + 5, oo = 0x3f3f3f3f;
int a[maxn];
typedef pair<int, int>P;
P p[maxn];
int n, k;
int ans;
int L, R;
void gao(int t)
{
int l = 0, r = 1;
ans = oo;
int res = 0;
while(l <= n && r <= n){
if(abs(p[r].first - p[l].first- t)< ans){
ans = abs(p[r].first - p[l].first - t);
L = p[l].second;
R = p[r].second;
res = abs(p[r].first - p[l].first);
}
if(p[r].first - p[l].first < t) r++;
else if(p[r].first - p[l].first > t) l++;
else break;
if(l == r) r++;
}
cout<<res<<‘ ‘<<min(L, R) + 1<<‘ ‘<<max(L, R)<<endl;
}
int main (void)
{
while(~scanf("%d%d", &n, &k) && (n + k)){
int sum = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
sum += a[i];
p[i] = P(sum, i);
}
p[0] = P(0, 0);
sort(p, p + n + 1);
int t;
for(int i = 0; i < k; i++){
scanf("%d", &t);
gao(t);
}
}
return 0;
}
用
可以把题意理解为向两个背包里面装东西,一个最后组成
那么有
状态转移方程就很好写了。
滚动数组可以优化。。但是只有500,我还是无脑的直接搞了。。
听
#include<cstdio>
#include<cstring>
#include<set>
#include<iostream>
using namespace std;
const int maxn = 5e2 + 5;
bool dp[maxn][maxn][maxn];
int a[maxn];
int main (void)
{
int n, m;scanf("%d%d",&n, &m);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
memset(dp, false, sizeof(dp));
dp[0][0][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = m; j >= 0; j--){
for(int k = j; k >= 0; k--){
dp[i][j][k] |= dp[i - 1][j][k];
if(j - a[i] >= k) dp[i][j][k] |= dp[i - 1][j - a[i]][k];
if(k >= a[i]){
dp[i][j][k] |= dp[i - 1][j - a[i]][k - a[i]];
}
}
}
}
set<int>v;
for(int j = 0; j <= m; j++){
if(dp[n][m][j]) v.insert(j);
}
printf("%d\n", v.size());
set<int>:: iterator s;
for(s = v.begin(); s != v.end(); s++){
printf("%d ", *s);
}
return 0;
}
一个软件有
有四种状态转移:
1. 发现的bug在已经发现的
2. 发现的bug在已经发现的
3. 发现的bug不在已有的
4. 发现的bug不属于已有的
状态转移方程:
然后整理一下即可。
/************************************************************************
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x
#define pl(x) cout<<#x<<":"<<x<<endl
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%I64d", &(n))
typedef long long ll;
const int maxn = 1e3 + 5, oo = 0x3f3f3f3f;
double dp[maxn][maxn];
int main (void)
{
int n, s;
while(~scanf("%d%d", &n, &s)){
memset(dp, 0, sizeof(dp));
for(int i = n; i >= 0; i--){
for(int j = s; j >= 0; j--){
if(i == n && j == s){
continue;
}
double a4 = 1.0 * n * s / (double)(n * s - i * j);
double a1 = 1.0 * (n - i) * (s - j) / (double) (n * s - i * j);
double a2 = 1.0 * (n - i) * j / (double)(n * s - i * j);
double a3 = 1.0 * i * (s - j) / (double) (n * s - i * j);
dp[i][j] = a4 + dp[i + 1][j + 1] * a1 + dp[i + 1][j] * a2 + dp[i][j + 1] * a3;
}
}
printf("%.4f\n", dp[0][0]);
}
return 0;
}
给定
首先求个凸包,将凸包的边平移
我们可以发现每个圆弧与其对应的凸包内角互补,凸边形内角和
所以我们只要求个凸包周长+圆的周长即可。
/*************************************************************************
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x
#define pl(x) cout<<#x<<":"<<x<<endl
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%I64d", &(n))
const int maxn = 1e3 + 5;
const double Pi = acos(-1.0);
struct Point
{
int x, y;
Point(){}
Point(int _x,int _y){
x = _x; y = _y;
}
Point operator -(const Point &b)const{
return Point(x - b.x, y - b.y);
}
int operator *(const Point &b)const{
return x * b.x + y * b.y;
}
int operator ^(const Point &b)const{
return x * b.y - y * b.x;
}
};
Point node[maxn], Stack[maxn];
inline double dist(Point p1, Point p2)
{
return sqrt((p2 - p1) * (p2 - p1));
}
int dot(Point p0, Point p1, Point p2)
{
return (p1 - p0) ^ (p2 - p0);
}
int top;
bool cmp(Point a, Point b)
{
if(a.x == b.x) return a.y < b.y;
else return a.x < b.x;
}
void convexHull(int n)
{
sort(node, node + n, cmp);
top = -1;
for(int i = 0; i < n; i++){
while(top > 0 && dot(Stack[top - 1], Stack[top], node[i]) <= 0) top--;
Stack[++top] = node[i];
}
int ttop = top;
for(int i = n - 2; i >= 0; i--){
while(top > ttop && dot(Stack[top - 1], Stack[top], node[i]) <= 0) top--;
Stack[++top] = node[i];
}
}
int main (void)
{
int N, L;sa(N);sa(L);
for(int i = 0; i < N; i++){
sa(node[i].x);sa(node[i].y);
}
convexHull(N);
double ans = 0;
for(int i = 0; i < top; i++){
ans += dist(Stack[i], Stack[i + 1]);
}
ans += 2 * Pi * L;
printf("%.f\n", ans);
return 0;
}
给定序列,由’(”)”[”’]”四个字符组成的字符串,求子序列最大匹配个数。
设
如果dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
枚举区间长度,更新对应长度的区间的
/*************************************************************************
> File Name: Q.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
using namespace std;
#define pr(x) cout<<#x<<":"<<x
#define pl(x) cout<<#x<<":"<<x<<endl
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%lld", &(n))
typedef long long ll;
typedef pair<int, int>p;
const int maxn = 1e2 + 5;
int dp[maxn][maxn];
int main (void)
{
string s;
while(cin>>s){
if(s == "end") break;
memset(dp, 0, sizeof(dp));
for(int len = 2; len <= s.length(); len++){
for(int j = 0, k = len - 1; j < k - len + 2 && k < s.length(); j++, k++){
if((s[j] == ‘(‘&& s[k] == ‘)‘) || (s[j] == ‘[‘ && s[k] == ‘]‘))
dp[j][k] = max(dp[j][k], dp[j + 1][k - 1] + 2);
for(int i = j + 1; i < k; i++)
dp[j][k] = max(dp[j][k], dp[j][i] + dp[i][k]);
}
}
cout<<dp[0][s.length() - 1]<<endl;
}
return 0;
}
给定剪刀、石头、布分别的人数,两种物种相遇,输的一方少一个人,问最后只剩下仅一种生物的概率。
概率
!!!最初计算相遇概率的时候除以的是从剩下所有人中选择两个人的方案数,其实不对,因为只有不同物种相遇才能使当前状态发生改变,所以需要进行归一化处理,即直接除以
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn = 1e2 + 5, oo = 0x3f3f3f3f;
double dp[maxn][maxn][maxn];
int main (void)
{
int r, s, p;cin>>r>>s>>p;
dp[r][s][p] = 1;
for(int i = r; i >= 0; i--){
for(int j = s; j >= 0; j--){
for(int k = p; k >= 0; k--){
if(i == 0 && j == 0 || i == 0 && k == 0|| j == 0 && k == 0) continue;
if(j >= 1) dp[i][j - 1][k] += dp[i][j][k] * i * j / (double)(i * j + j * k + i * k) ;
if(i >= 1) dp[i - 1][j][k] += dp[i][j][k] * i * k / (double)(i * j + j * k + i * k) ;
if(k >= 1) dp[i][j][k - 1] += dp[i][j][k] * j * k/ (double)(i * j + j * k + i * k) ;
}
}
}
double ans = 0;
for(int i = 1; i <= r; i++){
ans += dp[i][0][0];
}
printf("%.12f ", ans);
ans = 0;
for(int i = 1; i <= s; i++){
ans += dp[0][i][0];
}
printf("%.12f ", ans);
ans = 0;
for(int i = 1; i <= p; i++){
ans += dp[0][0][i];
}
printf("%.12f\n", ans);
return 0;
}
给定序列,若干查询,每个查询给定区间和
最初没看样例直接钦定输出每个不等于
对于一个区间,如果区间最大值和最小值相等,那么该区间元素值全部相同,那么我们维护区间的最大最小值,然后判断是否均等于
区间最大最小值用线段树维护,最初使用map来保存最大最小值所在的位置,结果TLE,改成数组就过了,就是内存难看了一点。。
感觉自己姿势怪怪的上网搜了一发标程:设
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
#define sa(n) scanf("%d", &(n))
const int maxn = 6e5 + 5, maxm = 1e6 + 5, oo = 0x3f3f3f3f;
struct Node{int l;int r;int a; int b;int pa;int pb;}Tree[maxn];
int a[maxn];
int ans[maxm];
void build(int i, int l, int r)
{
Tree[i].l = l;
Tree[i].r = r;
Tree[i].a = 0;
Tree[i].b = oo;
if(l == r) {
Tree[i].pa = Tree[i].pb = l;
return;
}
int mid = l + r >> 1;
build(i << 1, l, mid);
build((i << 1) | 1, mid + 1, r);
}
void push_up(int i)
{
if(Tree[i<<1].a > Tree[(i << 1)| 1].a){
Tree[i].a = Tree[i<<1].a ;
Tree[i].pa = Tree[i << 1].pa;
}else{
Tree[i].a = Tree[(i<<1) | 1].a ;
Tree[i].pa = Tree[(i << 1) | 1].pa;
}
if(Tree[i<<1].b < Tree[(i << 1)| 1].b){
Tree[i].b = Tree[i<<1].b ;
Tree[i].pb = Tree[i << 1].pb;
}else{
Tree[i].b = Tree[(i<<1) | 1].b ;
Tree[i].pb = Tree[(i << 1) | 1].pb;
}
}
int querymax(int i, int l, int r)
{
if(Tree[i].l == l && Tree[i].r == r){
ans[Tree[i].a] = Tree[i].pa;
return Tree[i].a;
}
int mid = Tree[i].l + Tree[i].r >> 1;
if(r <= mid) return querymax(i<<1, l, r);
else if(l > mid) return querymax((i << 1)|1, l, r);
else return max(querymax(i << 1, l, mid), querymax((i << 1)|1, mid + 1, r));
}
int querymin(int i, int l, int r)
{
if(Tree[i].l == l && Tree[i].r == r){
ans[Tree[i].b] = Tree[i].pb;
return Tree[i].b;
}
int mid = Tree[i].l + Tree[i].r >> 1;
if(r <= mid) return querymin(i<<1, l, r);
else if(l > mid) return querymin((i << 1)|1, l, r);
else return min(querymin(i << 1, l, mid), querymin((i << 1)|1, mid + 1, r));
}
void update(int i, int k, int x)
{
if(Tree[i].l == k && Tree[i].r == k){
Tree[i].a = Tree[i].b = x;
return;
}
int mid = Tree[i].l + Tree[i].r >> 1;
if(k <= mid) update(i << 1, k, x);
else update((i << 1) | 1, k, x);
push_up(i);
}
int main (void)
{
int n, m;sa(n);sa(m);
build(1, 0, n - 1);
for(int i = 0; i < n; i++){
sa(a[i]);
update(1, i, a[i]);
}
int l, r, x;
int a, b;
for(int i = 0; i < m; i++){
sa(l);sa(r);sa(x);
a = querymax(1, l - 1, r - 1);
b = querymin(1, l - 1, r - 1);
if(a == b && a == x) puts("-1");
else if(a == x) printf("%d\n", ans[b] + 1);
else printf("%d\n", ans[a] + 1);
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define sa(n) scanf("%d", &(n))
const int maxn = 6e5 + 5, maxm = 1e6 + 5, oo = 0x3f3f3f3f;
int dp[maxn];
int a[maxn];
int main (void)
{
int n, m;sa(n);sa(m);
memset(dp, -1, sizeof(dp));
for(int i = 1; i <= n; i++){
sa(a[i]);
if(a[i] == a[i - 1]) dp[i] = dp[i - 1];
else dp[i] = i - 1;
}
int l, r, t;
for(int i = 0; i < m; i++){
sa(l);sa(r);sa(t);
if(a[r] == t){
if(dp[r] < l) cout<<-1<<endl;
else cout<<dp[r]<<endl;
}else cout<<r<<endl;
}
return 0;
}
一堆石头,给定长宽高,每种石头均可以使用无数次,问这堆石头可以叠放的最高高度,要求下面的石头的长和宽分别严格大于上面石头的长和宽。
采用DAG最长路算法,由于长宽较大,不能直接用于表示状态,因此采用
/*************************************************************************
> Author: jiangyuzhu
> Mail: 834138558@qq.com
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<algorithm>
#include<stack>
#include<vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x
#define pl(x) cout<<#x<<":"<<x<<endl
#define sa(n) scanf("%d", &(n))
#define sal(n) scanf("%I64d", &(n))
typedef long long ll;
const int maxn = 30 + 5, oo = 0x3f3f3f3f;
int dp[maxn][maxn];
int p[maxn][3];
int u1, u2, l1, l2;
int n;
bool G[maxn][3][maxn][3];
bool check(int i, int k, int j, int q)// i 是否可以放在 j 上
{
if(k == 1) u1 = 2, u2 = 0;
if(k == 0) u1 = 2, u2 = 1;
if(k == 2) u1 = 1, u2 = 0;
if(q == 1) l1 = 2, l2 = 0;
if(q == 2) l1 = 1, l2 = 0;
if(q == 0) l1 = 1, l2 = 2;
int a1 = max(p[i][u1], p[i][u2]);
int a2 = min(p[i][u1], p[i][u2]);
int b1 = max(p[j][l1], p[j][l2]);
int b2 = min(p[j][l1], p[j][l2]);
return a1 < b1 && a2 < b2;
}
int get(int i, int k)
{
if(dp[i][k]) return dp[i][k];
int &ans = dp[i][k];
for(int a = 1; a <= n; a++){
for(int b = 0; b < 3; b++){
if(G[i][k][a][b])
ans = max(ans, get(a, b));
}
}
ans += p[i][k];
return ans;
}
int main (void)
{
int kas = 1;
while(~scanf("%d", &n) && n){
memset(dp, 0, sizeof(dp));
for(int i = 1; i <= n; i++){
sa(p[i][0]);sa(p[i][1]);sa(p[i][2]);
}
memset(G, false, sizeof(G));
for(int i = 1; i <= n; i++){
for(int k = 0; k < 3; k ++){
for(int j = 1; j <= n; j++){
for(int q = 0; q < 3; q++){
if(check(i, k, j, q)){
G[i][k][j][q] = true;
}
}
}
}
}
int ans = 0;
for(int i = 1; i <= n; i++){
for(int j = 0; j < 3; j++){
ans = max(ans, get(i, j));
}
}
cout<<"Case "<<kas++<<": maximum height = "<<ans<<endl;
}
return 0;
}
标签:
原文地址:http://blog.csdn.net/yukizzz/article/details/51865696