D1
题目
T1
一句话题意:解一个不定方程
,也就是说,先用
求出一组解
,那么解集是
。显然x只会取最小的正值或最大的负值。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,a,b,k,ans,x,y;
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int exgcd(int a,int b,int m,int &x,int &y){
if(!b) x=m/a,y=0;
else{
exgcd(b,a%b,m,x,y);
swap(x,y);
y-=(a/b)*x;
}
}
signed main(){
n=read(),a=read(),b=read();
int j;
k=gcd(a,b);
a/=k,b/=k;
if(a<b) swap(a,b);//使y的变化趋势比x大
for(int i=1;i<=n;i++){
j=read();
if(j%k){printf("-1");return 0;}
exgcd(a,b,j/k,x,y);
int tmp=(-y)/a+1;//ceil(y/a)
if(y<0){
x-=b*tmp;y+=a*tmp;//使x,y最接近0
}
x+=b*(y/a);y-=a*(y/a);//避免x或者y加过头
ans+=min(abs(x)+abs(y),abs(x+b)+abs(y-a));
}
printf("%lld",ans);
return 0;
}
注意:
求
时返回值一定要
,
不能省!,不然有些解就求不出来了
总结:题意很好提炼,重点在于怎么找出
,我们需要先求出一个特解之后考虑怎么让
逼近
,显然通解是
,我们只需找出
即可。(PS:为了避免+过头,需要
,反正如果
没超过
,
都不产生影响)
T2
pair(from solution):
考虑两个数对(ai,bi)和(aj,bj),如果ai<bj并且bi>aj,那么我们希望i排在
j前面。对于相反的情况,我们希望j排在i前面。其余两种情况i和j以任意
顺序排列都是相同的。显然按a+b从小到大排列就可以满足所有的需求。
确定顺序以后,我们设f[i][j]表示前i个数中,已选的数的max(a)=j,最
多选了多少个数。转移时只需要更新选了第i个数的情况,分别考虑j<=min(ai,bi)和ai<j<=bi,用线段树维护即可。
时间复杂度O(nlogn)
总结:这道题重点在于邻项交换的正确性,注意排序需满足严格弱序。然后用线段树维护 我线段树好菜啊TAT有没有大佬愿意拯救一下我
代码来自FSYorz
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e5 + 5;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt * 10 + (ch - '0'), ch = getchar();
return cnt * f;
}
int n, c[N << 1], siz, id[N];
struct node{ int a, b, w; bool operator < (cs node &A) cs{ return a + b < A.a + A.b; } }x[N];
typedef long long ll;
struct segmentree{
ll mx[N << 3], tg[N << 3];
#define mid ((l+r)>>1)
void pushup(int x){ mx[x] = max(mx[x<<1], mx[x<<1|1]); }
void pushnow(int x, ll v){ tg[x] += v; mx[x] += v; }
void pushdown(int x){ if(tg[x]) pushnow(x<<1, tg[x]), pushnow(x<<1|1, tg[x]), tg[x] = 0; }
void modify(int x, int l, int r, int p, ll v){
if(l == r){ mx[x] = max(mx[x], v); return; }
pushdown(x);
if(p <= mid) modify(x<<1, l, mid, p, v);
else modify(x<<1|1, mid+1, r, p, v);
pushup(x);
}
void add(int x, int l, int r, int L, int R, ll v){
if(L<=l && r<=R){ pushnow(x, v); return; }
pushdown(x);
if(L<=mid) add(x<<1, l, mid, L, R, v);
if(R>mid) add(x<<1|1, mid+1, r, L, R, v);
pushup(x);
}
ll query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return mx[x]; pushdown(x); ll ans = 0;
if(L<=mid) ans = max(ans, query(x<<1, l, mid, L, R));
if(R>mid) ans = max(ans, query(x<<1|1, mid+1, r, L, R));
return ans;
}
}seg;
int main(){
n = read();
for(int i = 1; i <= n; i++){
x[i].a = c[++siz] = read(), x[i].b = c[++siz] = read(), x[i].w = read();
} sort(c + 1, c + siz + 1); siz = unique(c + 1, c + siz + 1) - (c + 1);
for(int i = 1; i <= n; i++){
x[i].a = lower_bound(c + 1, c + siz + 1, x[i].a) - c;
x[i].b = lower_bound(c + 1, c + siz + 1, x[i].b) - c;
} sort(x + 1, x + n + 1);
for(int i = 1; i <= n; i++){
if(x[i].a >= x[i].b){
ll f = seg.query(1, 1, siz, 1, x[i].b);
seg.modify(1, 1, siz, x[i].a, f + x[i].w); // 单点修改 max
}
else{
ll f = seg.query(1, 1, siz, 1, x[i].a);
seg.modify(1, 1, siz, x[i].a, f + x[i].w);
seg.add(1, 1, siz, x[i].a + 1, x[i].b, x[i].w);
}
} cout << seg.mx[1]; return 0;
}
T3
distance:
以所有特殊点为起点跑多源最短路,并且记录每个点是由哪个源点拓展的。然后枚举所有边,如果边的两端是由不同源点拓展的,就更新这两个点的答案。不难证明,对于源点 ,由 拓展的点 以及与 相邻且不由 拓展的点 ,如果 的最优路径从 走到了 ,那么走到拓展 的源点是最优的。因此这个做法是正确的。
时间复杂度
总结:考试时想到了
,但是觉得有点扯 ? 就没有写,以后要好好分析每个思路的可行性啊。
思路:
- 初始化每个特殊点的距离为 和前驱节点为自己
- 把每个点放进队列里
- 以每个点为源点跑最短路
- 更新前驱节点到每个点的最短距离
- 输出答案
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N=200010;
int n,m,p,u[N],pre[N];
long long dis[N],ans[N];
vector<int> edge[N],val[N];
set<pair<long long,int> >f;
pair<long long,int> w;
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
int main(){
int i,j,k,l;
n=read(),m=read(),p=read();
for(int i=1;i<=p;i++) u[i]=read();
for(int i=1;i<=m;i++){
j=read(),k=read(),l=read();
edge[j].push_back(k);
val[j].push_back(l);
edge[k].push_back(j);
val[k].push_back(l);
}
for(int i=1;i<=n;i++) dis[i]=ans[i]=1e18;
for(int i=1;i<=p;i++) dis[u[i]]=0,pre[u[i]]=u[i];
for(int i=1;i<=n;i++) f.insert(make_pair(dis[i],i));
while(!f.empty()){
w=*f.begin();f.erase(w);i=w.second;
for(j=0;j<edge[i].size();j++){
k=edge[i][j];l=val[i][j];
if(dis[k]>dis[i]+l){
f.erase(make_pair(dis[k],k)); dis[k]=dis[i]+l;pre[k]=pre[i];f.insert(make_pair(dis[k],k));
}
}
}
for(i=1;i<=n;i++)
for(j=0;j<edge[i].size();j++)
if(pre[i]!=pre[edge[i][j]])
ans[pre[i]]=min(ans[pre[i]],dis[i]+val[i][j]+dis[edge[i][j]]);
for(int i=1;i<=p;i++)
printf("%lld ",ans[u[i]]);
return 0;
}
不知道为什么我跑了 ,加了 也不快,难道我自带大常数???