题目就是2017NOI Day 1三道题。
能量采集
超级钢琴
海拔
第一题:用欧拉函数性质:一个数的所有因数的欧拉函数之和即该数,也可以用容斥原理。
再说一遍:大int乘以大int一定要转longlong!!!!!!!!
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e5+5;
int n,m,prime[MAXN],tot=0;
bool vis[MAXN];
ll ans,phi[MAXN];
inline void linear_shaker() {
memset(vis,false,sizeof(vis));
phi[1]=1;
for (register int i=2;i<MAXN;++i) {
if (!vis[i]) prime[++tot]=i,phi[i]=i-1;
for (int j=1;j<=tot&&i*prime[j]<MAXN;++j) {
vis[i*prime[j]]=true;
if (i%prime[j]==0) {phi[i*prime[j]]=phi[i]*prime[j];break;}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
for (register int i=2;i<MAXN;++i) phi[i]+=phi[i-1];
}
int main() {
freopen("energy.in","r",stdin);
freopen("energy.out","w",stdout);
linear_shaker();
while (~scanf("%d%d",&n,&m)) {
ans=0;
int last=0,t=min(n,m);
for (int i=1;i<=t;i=last+1) {
last=min(n/(n/i),m/(m/i));
ans+=(phi[last]-phi[i-1])*(n/i)*(m/i);
}
cout<<(ans<<1)-1ll*n*m<<endl;//1ll?????!!!!!
}
return 0;
}
第二题:RMQ+堆
新技能get!如何求长度为L~R的最大连续子段和
固定左端点 i,用st表维护每一个[i,i+L-1]~[i,i+R-1]的前缀和最大值
然后用大根堆维护,每取一个元素,增加两个可行元素(具体见代码)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int ,int > pa;
const int MAXN=5e5+2;
pa mx[MAXN][21];int lg[MAXN];
int n,K,L,R,sum[MAXN];
ll ans=0;
struct NODE {
int id,l,r,w,p;
NODE(int _id,int _l,int _r,int _w,int _p):id(_id),l(_l),r(_r),w(_w),p(_p) {}
friend bool operator <(const NODE &a,const NODE &b) {
return a.w<b.w;
}
};
priority_queue<NODE> q;
void init_st() {
for (register int i=1;i<=n;++i)
mx[i][0].first=sum[i],mx[i][0].second=i,lg[i]=(int)log2(i);
for (int i=1;(1<<i)<=n;++i)
for (register int j=1;j+(1<<i)-1<=n;++j)
mx[j][i]=max(mx[j][i-1],mx[j+(1<<i-1)][i-1]);
}
pa query(int l,int r) {
int t=lg[r-l+1];
return max(mx[l][t],mx[r-(1<<t)+1][t]);
}
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int main() {
freopen("piano.in","r",stdin);
freopen("piano.out","w",stdout);
n=read(),K=read(),L=read(),R=read(),sum[0]=0;
for (register int i=1;i<=n;++i) sum[i]=read()+sum[i-1];
init_st();
for (register int i=1;i+L-1<=n;++i) {
int l=i+L-1,r=min(n,i+R-1);
pa t=query(l,r);
q.push(NODE(i,l,r,t.first-sum[i-1],t.second));
}
for (register int f=0;f<K;++f) {
NODE nd=q.top();q.pop();
ans+=nd.w;
if (nd.l<nd.p) {
pa t=query(nd.l,nd.p-1);
q.push(NODE(nd.id,nd.l,nd.p-1,t.first-sum[nd.id-1],t.second));
}
if (nd.p<nd.r) {
pa t=query(nd.p+1,nd.r);
q.push(NODE(nd.id,nd.p+1,nd.r,t.first-sum[nd.id-1],t.second));
}
}
cout<<ans<<endl;
return 0;
}
第三题:平面图最小割转对偶图最短路
这个还真不会,只是在bzoj1001见过(没做)。目前只能感性理解,详细讲解戳这里
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int MAXN=505,INF=0x3f3f3f3f;
int n,dis[MAXN*MAXN],source,sink,t;
int head[MAXN*MAXN],edge=0;
struct EDGE {
int v,nxt,w;
}e[MAXN*MAXN<<2];
bool vis[MAXN*MAXN];
struct NODE {
int id,dis;
NODE (int _id,int _dis):id(_id),dis(_dis) {}
friend bool operator <(const NODE &a,const NODE &b) {
return a.dis>b.dis;
}
};
priority_queue<NODE> q;
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
inline void adde(int u,int v,int w) {
e[edge].nxt=head[u],e[edge].v=v,e[edge].w=w,head[u]=edge++;
}
inline int num(int x,int y) {
if (y==0||x==n+1) return source;
if (x==0||y==n+1) return sink;
return (x-1)*n+y;
}
inline void dijkstra() {
memset(vis,false,sizeof(vis));
memset(dis,127/3,sizeof(dis));
dis[source]=0,q.push(NODE(source,0));
while (!q.empty()) {
NODE a=q.top();q.pop();
int p=a.id;
if (!vis[p]) {
vis[p]=true;
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (dis[v]>dis[p]+e[i].w) {
dis[v]=dis[p]+e[i].w;
q.push(NODE(v,dis[v]));
}
}
}
}
}
int main() {
freopen("altitude.in","r",stdin);
freopen("altitude.out","w",stdout);
memset(head,-1,sizeof(head));
n=read(),source=0,sink=n*n+1;
for (int i=0;i<=n;++i) for (int j=1;j<=n;++j) t=read(),adde(num(i+1,j),num(i,j),t);
for (int i=1;i<=n;++i) for (int j=0;j<=n;++j) t=read(),adde(num(i,j),num(i,j+1),t);
for (int i=0;i<=n;++i) for (int j=1;j<=n;++j) t=read(),adde(num(i,j),num(i+1,j),t);
for (int i=1;i<=n;++i) for (int j=0;j<=n;++j) t=read(),adde(num(i,j+1),num(i,j),t);
dijkstra();
printf("%d\n",dis[sink]);
return 0;
}