裸的完全背包……被孙到
把
2
i
2^i
2i当作物品,容量为
n
n
n
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define mod 1000000007
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=1000005;
int n,dp[Size],LOG[Size];
int main() {
// freopen("T1.in","r",stdin);
n=read();
dp[0]=1;
for(re i=0; i<=20; i++) {
for(re j=0; j+(1<<i)<=n; j++) {
dp[j+(1<<i)]=(dp[j+(1<<i)]+dp[j])%mod;
}
}
printf("%d",dp[n]);
return 0;
}
T2 贫富差距
u
,
v
u,v
u,v是朋友就建一条
u
,
v
u,v
u,v的边。
如果所有的点都在同一个联通块中,那么贫富差距的最大值就是最长链的长度乘以
d
d
d(易证无法构造出更大的贫富差距)。
判-1用并查集,判是否所有的点在同一个联通块中即珂,如果有两个点不在同一个联通块中,则这两个点的贫富差距珂以无限扩大。
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define mod 1000000007
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline char GetChar() {
char ch=getchar();
while(ch!='Y' && ch!='N') ch=getchar();
return ch;
}
const int Size=105;
int n,d,father[Size];
int Find(int x) {
if(x==father[x]) return x;
return father[x]=Find(father[x]);
}
int cnt,head[Size];
struct node {
int v,next;
} w[Size*Size<<1];
inline void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
int Queue[Size],dis[Size];
bool vis[Size];
int bfs(int s) {
memset(vis,0,sizeof(vis));
int hd=0,tl=0;
Queue[++tl]=s;
vis[s]=true;
dis[s]=0;
int ans=0;
while(hd<tl) {
int x=Queue[++hd];
for(re i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(!vis[nxt]) {
vis[nxt]=true;
Queue[++tl]=nxt;
dis[tl]=dis[hd]+1;
if(dis[tl]>ans) {
ans=dis[tl];
}
}
}
}
return ans;
}
inline void init() {
memset(head,0,sizeof(head));
cnt=0;
for(re i=1; i<=n; i++) {
father[i]=i;
}
}
int main() {
// freopen("T2.in","r",stdin);
int T=read();
while(T--) {
n=read();
d=read();
init();
for(re i=1; i<=n; i++) {
for(re j=1; j<=n; j++) {
char ch=GetChar();
if(ch=='Y') {
AddEdge(i,j);
int fx=Find(i);
int fy=Find(j);
if(fx!=fy) {
father[fx]=fy;
}
}
}
}
int fa=Find(1);
bool inf=false;
for(re i=2; i<=n; i++) {
if(Find(i)!=fa) {
inf=true;
break;
}
}
if(inf) {
puts("-1");
continue;
}
int ans=0;
for(re i=1; i<=n; i++) {
int now=bfs(i);
if(now>ans) {
ans=now;
}
}
printf("%d\n",ans*d);
}
return 0;
}
T3 特殊的排列
找出序列上相邻两项差为1的最长子序列,则这个子序列长度是最长的不用移动的长度。
证明:
1.移动一个子序列之外的数,子序列的相对顺序不变。
2.不在最长子序列中的数一定要移动到序列首或序列末(否则珂以形成更长的相邻两项差为1的子序列)。
3.不存在另一个更长的子序列,相邻两项差不为1,且这个子序列中的数不用移动。因为若相邻两项差不为1,那么这两项之间的数必须要通过移动子序列里的数来得到。
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=50005;
int dp[Size];
int main() {
int n=read();
int ans=0;
for(re i=1; i<=n; i++) {
int x=read();
if((dp[x]=dp[x-1]+1)>ans) {
ans=dp[x];
}
}
printf("%d",n-ans);
return 0;
}
T4 极品飞车
一个特殊的隐含条件是速度必须要>0,否则时间会变成负数或正无穷(长者:只有我才能操控负数和
+
∞
+\infin
+∞的时间,你们还是太naive)
列出式子,
d
1
s
1
+
c
+
d
2
s
2
+
c
+
.
.
.
+
d
n
s
n
+
c
=
t
\frac{d_1}{s_1+c}+\frac{d_2}{s_2+c}+...+\frac{d_n}{s_n+c}=t
s1+cd1+s2+cd2+...+sn+cdn=t
因为对于任意
i
i
i,有
s
i
+
c
>
0
s_i+c>0
si+c>0,所以左边是单调递减的。
所以二分
c
c
c,暴力判即珂。
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define mod 1000000007
#define eps 1e-10
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
inline char GetChar() {
char ch=getchar();
while(ch!='Y' && ch!='N') ch=getchar();
return ch;
}
const int Size=1005;
int n;
double t,s[Size],d[Size];
double check(double mid) {
double ans=0;
for(re i=1; i<=n; i++) {
if(fabs(s[i]+mid)<eps) {
ans+=1e15;
} else {
ans+=d[i]/(s[i]+mid);
}
}
return ans;
}
int main() {
// freopen("T4.in","r",stdin);
n=read();
scanf("%lf",&t);
double minn=1e10;
for(re i=1; i<=n; i++) {
scanf("%lf %lf",&d[i],&s[i]);
if(s[i]<minn) minn=s[i];
}
double l=-minn+eps,r=1e7,mid;
for(re i=1; i<=1000; i++) {
mid=(l+r)/2;
if(check(mid)>=t) {
l=mid;
} else {
r=mid;
}
}
printf("%.6lf",l);
return 0;
}
/*
3 9
3 2
6 3
9 4
*/
T5 修改数组
首先把所有
a
[
i
]
a[i]
a[i]都减去
i
i
i,然后题目变成修改一些数,要求改成非负整数,且满足序列不减。
把所有
a
[
i
]
>
=
0
a[i]>=0
a[i]>=0的
a
[
i
]
a[i]
a[i]提取出来,跑最长不降子序列。因为序列不减,所以剩下的
a
[
i
]
<
0
a[i]<0
a[i]<0的部分一定能改成一个非负整数,使序列不减。
最后只有这个LIS的所有元素不用修改。
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=100005;
int n,maxn,a[Size],b[Size],tree[Size];
inline int lowbit(int x) {
return x&(-x);
}
inline void update(int x,int v) {
for(re i=x; i<=maxn; i+=lowbit(i)) {
if(v>tree[i]) {
tree[i]=v;
}
}
}
inline int query(int x) {
int ans=0;
for(re i=x; i; i-=lowbit(i)) {
if(tree[i]>ans) {
ans=tree[i];
}
}
return ans;
}
int main() {
n=read();
int tot=0;
for(re i=1; i<=n; i++) {
a[i]=read()-i;
if(a[i]>=0) {
b[++tot]=a[i];
}
}
for(re i=1; i<=tot; i++) {
a[i]=b[i];
}
sort(a+1,a+1+tot);
maxn=unique(a+1,a+1+tot)-(a+1);
for(re i=1; i<=tot; i++) {
b[i]=lower_bound(a+1,a+1+maxn,b[i])-a;
}
int ans=0;
for(re i=1; i<=tot; i++) {
int x=query(b[i]);
update(b[i],x+1);
if(x+1>ans) {
ans=x+1;
}
}
printf("%d",n-ans);
return 0;
}
/*
12
3 3 6 6 1 2 6 1 1 3 6 4
*/
T6 小Biu看电影
不知道为什么珂以暴力dfs……
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define mod 1000000007
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=300005;
const int INF=0x3f3f3f3f;
int n,m,cnt,head[Size];
struct Edge {
int v,t,next;
} w[Size<<1];
void AddEdge(int u,int v,int c) {
w[++cnt].v=v;
w[cnt].t=c;
w[cnt].next=head[u];
head[u]=cnt;
}
int ans=INF,val[Size],dis[Size];
void dfs(int x,int sum) {
if(sum>=ans) return;
if(sum+val[x]<ans) {
ans=sum+val[x];
}
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(sum+w[i].t<dis[nxt]) {
dis[nxt]=sum+w[i].t;
dfs(nxt,sum+w[i].t);
}
}
}
int main() {
// freopen("T6.in","r",stdin);
n=read();
m=read();
for(re i=1; i<=n; i++) {
val[i]=read();
}
for(re i=1; i<=m; i++) {
int u=read();
int v=read();
int c=read()<<1;
AddEdge(u,v,c);
AddEdge(v,u,c);
}
for(re i=1; i<=n; i++) {
memset(dis,0x3f,sizeof(dis));
ans=val[i];
dfs(i,0);
printf("%d\n",ans);
}
return 0;
}
正解:
建一个虚点
s
s
s,从
s
s
s向所有
i
i
i连边权为
v
a
l
[
i
]
val[i]
val[i]的边,跑dijkstra。
然后
s
s
s到每个点的距离就是从这个点开始能看到电影的最小花费(容易看出最小花费就是
s
s
s到一个点的最短距离,因为一开始建了边权为
v
a
l
[
i
]
val[i]
val[i]的边)。
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<queue>
#define re register int
#define mod 1000000007
using namespace std;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=100005;
const int INF=0x3f3f3f3f;
int n,m,cnt,head[Size];
struct Edge {
int v,t,next;
} w[600005];
void AddEdge(int u,int v,int c) {
w[++cnt].v=v;
w[cnt].t=c;
w[cnt].next=head[u];
head[u]=cnt;
}
struct node {
int x,t;
};
inline bool operator < (const node a,const node b) {
return a.t>b.t;
}
int dis[Size];
void Dijkstra(int s) {
memset(dis,0x3f,sizeof(dis));
priority_queue<node> Q;
Q.push((node){s,0});
dis[s]=0;
while(!Q.empty()) {
int x=Q.top().x;
Q.pop();
for(re i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(dis[x]+w[i].t<dis[nxt]) {
dis[nxt]=dis[x]+w[i].t;
Q.push((node){nxt,dis[nxt]});
}
}
}
}
int main() {
// freopen("T6.in","r",stdin);
n=read();
m=read();
int s=n+1;
for(re i=1; i<=n; i++) {
int x=read();
AddEdge(s,i,x);
}
for(re i=1; i<=m; i++) {
int u=read();
int v=read();
int c=read()<<1;
AddEdge(u,v,c);
AddEdge(v,u,c);
}
Dijkstra(s);
for(re i=1; i<=n; i++) {
printf("%d\n",dis[i]);
}
return 0;
}