[BZOJ 3339]Rmq Problem
万恶的标题党啊~
这个题明显扫描线辣……但是怎么扫呢?
我们可以考虑对于每个前缀区间[tex][1,r][/tex]先求出他们的[tex]mex[/tex]值,然后用线段树维护。
从[tex][l,r][/tex]转移到[tex][l+1,r][/tex]时,需要将[tex][l,next[l]-1][/tex]([tex]next[l][/tex]是下一个和[tex]l[/tex]具有相同值的位置)中的所有大于[tex]A[l][/tex]([tex]l[/tex]的值)的值全部变为[tex]A[l][/tex]。但是这一步怎么做啊……难道要用Segment Beats?
答案是否定的。注意到我们真正需要的只是叶子结点,所以其他非叶子结点的最小值全部弄成无限大,然后这样做就可以把最小值当做标记来弄了……非常精巧。
代码:
/**************************************************************
Problem: 3339
User: danihao123
Language: C++
Result: Accepted
Time:1988 ms
Memory:10396 kb
****************************************************************/
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int maxn=200005,INF=0x7f7f7f7f;
const int maxnode=maxn*4;
int A[maxn];
int minv[maxnode];
void build_tree(int o,int L,int R){
if(L==R){
minv[o]=A[L];
}else{
int M=L+(R-L)/2;
minv[o]=INF;
build_tree(o<<1,L,M);
build_tree(o<<1|1,M+1,R);
}
}
inline void paint(int o,int v){
minv[o]=min(minv[o],v);
}
inline void pushdown(int o){
paint(o<<1,minv[o]);
paint(o<<1|1,minv[o]);
minv[o]=INF;
}
int pos;
int query(int o,int L,int R){
if(L==R)
return minv[o];
if(minv[o]<INF)
pushdown(o);
int M=L+(R-L)/2;
if(pos<=M)
return query(o<<1,L,M);
else
return query(o<<1|1,M+1,R);
}
int ql,qr,v;
void update(int o,int L,int R){
if(ql<=L && R<=qr){
paint(o,v);
}else{
if(minv[o]<INF)
pushdown(o);
int M=L+(R-L)/2;
if(ql<=M)
update(o<<1,L,M);
if(qr>M)
update(o<<1|1,M+1,R);
}
}
inline int readint(){
register int x=0;
register char c;
fread(&c,1,1,stdin);
while(!isdigit(c))
fread(&c,1,1,stdin);
while(isdigit(c)){
x=x*10+c-'0';
fread(&c,1,1,stdin);
}
return x;
}
int buf[21];
inline void putint(int x){
register int i,p=0;
if(!x){
buf[p++]=0;
}else{
while(x){
buf[p++]=x%10;
x/=10;
}
}
for(i=p-1;i>=0;i--)
putchar(buf[i]+'0');
putchar('\n');
}
bool vis[maxn];
int last[maxn],next[maxn];
struct Query{
int id;
int l,r;
int ans;
};
bool cmp1(const Query& x,const Query& y){
if(x.l==y.l)
return x.r<y.r;
else
return x.l<y.l;
}
bool cmp2(const Query& x,const Query& y){
return x.id<y.id;
}
Query QS[maxn];
int V[maxn];
int main(){
int n,q;
register int i,l=1,k=0;
n=readint();
q=readint();
for(i=1;i<=n;i++){
V[i]=readint();
vis[V[i]]=true;
if(k==V[i])
while(vis[k])
k++;
A[i]=k;
}
build_tree(1,1,n);
for(i=n;i>=1;i--){
if(!last[V[i]])
next[i]=n+1;
else
next[i]=last[V[i]];
last[V[i]]=i;
}
for(i=1;i<=q;i++){
QS[i].id=i;
QS[i].l=readint();
QS[i].r=readint();
}
sort(QS+1,QS+1+q,cmp1);
for(i=1;i<=q;i++){
while(l<QS[i].l){
ql=l;
qr=next[l]-1;
v=V[l];
update(1,1,n);
l++;
}
pos=QS[i].r;
QS[i].ans=query(1,1,n);
}
sort(QS+1,QS+1+q,cmp2);
for(i=1;i<=q;i++){
putint(QS[i].ans);
}
return 0;
}
[BZOJ 1468]Tree
点分治第一题……
这个也就是最经典的那个点分治问题了吧……参照qzc大爷的论文吧。
代码:
/**************************************************************
Problem: 1468
User: danihao123
Language: C++
Result: Accepted
Time:816 ms
Memory:2736 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 40005;
const int maxm = maxn * 2;
struct Edge {
Edge *next;
int to, dist;
};
Edge pool[maxm];
int graph_cnt;
Edge *first[maxn];
inline void clear_graph() {
graph_cnt = 0;
memset(first, 0, sizeof first);
}
inline void add_edge(int u, int v, int d) {
Edge *e = &pool[graph_cnt++];
e->next = first[u];
first[u] = e;
e->to = v;
e->dist = d;
}
int k;
int calc(vector<int>& V) {
int size = V.size();
int rc = size - 1;
int ans = 0;
if(size <= 1){
return 0;
}
for(int i = 0; i < size; i++) {
while(i < rc && V[i] + V[rc] > k) {
rc--;
}
if(i == rc) {
break;
}
ans += rc - i;
}
return ans;
}
bool vis[maxn];
int siz[maxn];
void gen_siz(int x, int fa) {
siz[x] = 1;
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
gen_siz(v, x);
siz[x] += siz[v];
}
}
}
int now_root, best_root;
void gen_best_root(int x, int fa) {
bool OK = siz[x]*2 >= siz[now_root];
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
gen_best_root(v, x);
OK = OK && (siz[v]*2 <= siz[now_root]);
}
}
if(OK) {
best_root = x;
}
}
void add_to_V(int x, int fa, int d, vector<int>& V) {
V.push_back(d);
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(!vis[v] && v != fa) {
add_to_V(v, x, d + e->dist, V);
}
}
}
int divide(int x) {
gen_siz(x, 0);
now_root = x;
gen_best_root(x, 0);
x = best_root;
vis[x] = true;
vector<int> V, CV;
int ans = 0;
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(vis[v]) {
continue;
}
CV.clear();
add_to_V(v, x, e->dist, CV);
sort(CV.begin(), CV.end());
ans -= calc(CV);
for(int i = 0; i < CV.size(); i++) {
V.push_back(CV[i]);
}
}
V.push_back(0);
sort(V.begin(), V.end());
ans += calc(V);
for(Edge *e = first[x]; e; e = e->next) {
int v = e->to;
if(vis[v]) {
continue;
}
ans += divide(v);
}
return ans;
}
int main() {
int n;
scanf("%d", &n);
clear_graph();
for(int i = 1; i <= (n - 1); i++) {
int u, v, d;
scanf("%d%d%d", &u, &v, &d);
add_edge(u, v, d);
add_edge(v, u, d);
}
scanf("%d", &k);
printf("%d\n", divide(1));
return 0;
}
[BZOJ 1051]受欢迎的牛
Tarjan缩点第一题……
很容易想到找出度为0的点(当且仅当这样的点有一个时有解),但是在有环图上这个方法会失效。
处理方法是,用Tarjan的强联通分量算法求出所有SCC,然后把每个SCC缩成一个点,然后再在缩点之后的图上求解即可(这个图是DAG。原因很简单,要是有环还能接着缩)。
代码:
/**************************************************************
Problem: 1051
User: danihao123
Language: C++
Result: Accepted
Time:72 ms
Memory:1692 kb
****************************************************************/
#include <cstdio>
#include <stack>
#include <algorithm>
using namespace std;
const int maxn=10005,maxm=50005;
int first[maxn];
int next[maxm],to[maxm];
int graph_cnt=0;
inline void AddEdge(int u,int v){
graph_cnt++;
next[graph_cnt]=first[u];
first[u]=graph_cnt;
to[graph_cnt]=v;
}
int pre[maxn],low[maxn],sccno[maxn],sccsiz[maxn];
stack<int> S;
int dfs_clk=0,scc_cnt=0;
void dfs(int x){
dfs_clk++;
pre[x]=low[x]=dfs_clk;
S.push(x);
int i,u;
for(i=first[x];i;i=next[i]){
u=to[i];
if(!pre[u]){
dfs(u);
low[x]=min(low[x],low[u]);
}else{
if(!sccno[u])
low[x]=min(low[x],pre[u]);
}
}
if(low[x]==pre[x]){
scc_cnt++;
sccsiz[scc_cnt]=0;
while(true){
u=S.top();
S.pop();
sccno[u]=scc_cnt;
sccsiz[scc_cnt]++;
if(u==x)
break;
}
}
}
int n;
inline void Tarjan(){
register int i;
for(i=1;i<=n;i++)
if(!pre[i])
dfs(i);
}
bool Out[maxn];
int main(){
int m,u,v;
register int i,j,ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%d%d",&u,&v);
AddEdge(u,v);
}
Tarjan();
for(i=1;i<=n;i++){
for(j=first[i];j;j=next[j]){
u=to[j];
if(sccno[i]!=sccno[u])
Out[sccno[i]]=true;
}
}
for(i=1;i<=scc_cnt;i++){
if(!Out[i]){
if(ans){
ans=0;
break;
}
ans=sccsiz[i];
}
}
if(n==1)
ans=0;
printf("%d\n",ans);
return 0;
}
[BZOJ 3172]单词
首先……出题人语文不太好……估计和我这种语文很差的人还有差距……
这个提的意思是,给你若干个单词,输出每个单词在所有单词(而不是把所有单词连起来)中出现的次数。
然后肯定要AC自动机上失配函数搞一波辣……把BFS序倒过来搜,传递出现次数即可。还有这个题的数据范围好像也比给出的小很多……
代码:
/**************************************************************
Problem: 3172
User: danihao123
Language: C++
Result: Accepted
Time:232 ms
Memory:115080 kb
****************************************************************/
#include <cstdio>
#include <cstring>
const int maxn=205;
const int maxnode=1*1e6+5;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
#define CL_ARR(x,v) memset(x,v,sizeof(x))
int cnt[maxnode];
namespace ACAutomate{
// Trie
const int sigma_size=26;
int Tree[maxnode][sigma_size];
int siz;
inline int idx(char c){
return c-'a';
}
void InitAC(){
siz=0;
CL_ARR(Tree[0],0);
}
int Insert(char *s){
register int i,n=strlen(s);
register int u=0,c;
REP(i,n){
c=idx(s[i]);
if(!Tree[u][c]){
siz++;
Tree[u][c]=siz;
CL_ARR(Tree[siz],0);
}
u=Tree[u][c];
cnt[u]++;
}
return u;
}
// AC Automate
int f[maxnode];
int Q[maxnode];
void InitFail(){
register int i,u,x,v,head=0,tail=0;
f[0]=0;
REP(i,sigma_size){
u=Tree[0][i];
if(u){
f[u]=0;
Q[tail++]=u;
}
}
while(head<tail){
u=Q[head];
head++;
REP(i,sigma_size){
x=Tree[u][i];
if(!x){
continue;
}
Q[tail++]=x;
v=f[u];
while(v && !Tree[v][i])
v=f[v];
f[x]=Tree[v][i];
}
}
for(i=tail-1;i>=0;i--)
cnt[f[Q[i]]]+=cnt[Q[i]];
}
};
char buf[maxnode];
int pos[maxn];
int main(){
int n;
register int i;
scanf("%d",&n);
ACAutomate::InitAC();
REP_B(i,n){
scanf("%s",buf);
pos[i]=ACAutomate::Insert(buf);
}
ACAutomate::InitFail();
REP_B(i,n){
printf("%d\n",cnt[pos[i]]);
}
return 0;
}
[BZOJ 1511]Periods of Words
很妙的一道题啊。
这个题可以先求一遍KMP的失配函数,然后对于每个失配函数沿着失配边往前一直走(不能走到0)。然后对于每个前缀[tex]i[/tex],答案就是[tex]i-f[i][/tex](要求[tex]f[i][/tex]不为0,反之无解)。
为什么这样可以呢?
首先建议大家看一下Matrix67关于KMP的解释。对于一个前缀,如果整个弄上去肯定不行。所以说需要前面和后面重叠一个字串,这个子串是不考虑的。当然,我们希望在这个被删除的子串最短辣。
代码:
/**************************************************************
Problem: 1511
User: danihao123
Language: C++
Result: Accepted
Time:148 ms
Memory:5704 kb
****************************************************************/
#include <cstdio>
const int maxn=1000005;
char buf[maxn];
int f[maxn];
int main(){
register int i,j;
register long long ans=0;
int n;
scanf("%d%s",&n,buf);
f[0]=f[1]=0;
for(i=1;i<n;i++){
j=f[i];
while(j && buf[i]!=buf[j])
j=f[j];
f[i+1]=(buf[i]==buf[j]?j+1:0);
}
for(i=2;i<=n;i++){
if(f[i]){
while(f[f[i]]){
f[i]=f[f[i]];
}
}
}
for(i=1;i<=n;i++){
if(f[i]){
ans+=i-f[i];
}
}
printf("%lld\n",ans);
return 0;
}
[BZOJ 1212]L语言
这个感觉本质上和那个Remember a Word很像吧……
不过这个只是求最长可行前缀,递推即可。
代码:
/**************************************************************
Problem: 1212
User: danihao123
Language: C++
Result: Accepted
Time:660 ms
Memory:45072 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=1048581;
const int maxW=4005,maxL=105;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
#define DREP(i,n) for(i=(n)-1;i>=0;i--)
#define CL_ARR(x,v) memset(x,v,sizeof(x))
vector<int> AnsList;
namespace Trie{
const int maxnode=400005;
const int sigma_siz=26;
int Tree[maxnode][sigma_siz];
int val[maxnode];
int siz;
inline int idx(char c){
return c-'a';
}
inline void InitTrie(){
siz=0;
val[0]=0;
CL_ARR(Tree[0],0);
}
void Insert(char *s,int v){
register int u=0,n=strlen(s);
register int i,c;
REP(i,n){
c=idx(s[i]);
if(!Tree[u][c]){
siz++;
Tree[u][c]=siz;
val[siz]=0;
CL_ARR(Tree[siz],0);
}
u=Tree[u][c];
}
val[u]=v;
}
void Query(char *s,int len){
register int i,c,u=0;
AnsList.clear();
REP(i,len){
if(!s[i])
break;
c=idx(s[i]);
if(!Tree[u][c])
break;
u=Tree[u][c];
if(val[u])
AnsList.push_back(val[u]);
}
}
};
char Text[maxn];
int len[maxW];
char buf[maxL];
bool d[maxn];
int main(){
int n,m;
int length;
register int i,j,k;
bool flag;
Trie::InitTrie();
scanf("%d%d",&n,&m);
REP_B(i,n){
scanf("%s",buf);
len[i]=strlen(buf);
Trie::Insert(buf,i);
}
REP_B(i,m){
scanf("%s",Text);
length=strlen(Text);
CL_ARR(d,0);
d[0]=true;
for(j=0;j<=length;j++){
if(d[j]){
Trie::Query(Text+j,length-j);
REP(k,AnsList.size()){
d[j+len[AnsList[k]]]=true;
}
}
}
flag=false;
for(j=length;j>=0;j--){
if(d[j]){
flag=true;
printf("%d\n",j);
break;
}
}
if(!flag)
puts("0");
}
return 0;
}
[BZOJ 2081]Beads
Hash第一题……
这个题直接枚举串长Hash判断即可。不过,注意读题。
感觉Hash挺简单的。还有就是我用了wyy的生日做Hash种子辣!
代码:
/**************************************************************
Problem: 2081
User: danihao123
Language: C++
Result: Accepted
Time:5636 ms
Memory:10904 kb
****************************************************************/
#include <cstdio>
#include <set>
#include <vector>
using namespace std;
typedef unsigned long long ull;
const int maxn=200005;
const ull x=200261;
ull s[maxn];
ull sufHash[maxn],preHash[maxn],x_pow[maxn];
int n;
inline void InitHash(){
register int i;
for(i=n;i>=1;i--){
sufHash[i]=s[i]+sufHash[i+1]*x;
}
for(i=1;i<=n;i++){
preHash[i]=s[i]+preHash[i-1]*x;
}
x_pow[0]=1;
for(i=1;i<=n;i++){
x_pow[i]=x*x_pow[i-1];
}
}
inline ull Hash(int i,int L){
return sufHash[i]-x_pow[L]*sufHash[i+L];
}
inline ull FilpHash(int i,int L){
return preHash[i+L-1]-x_pow[L]*preHash[i-1];
}
set<ull> S;
vector<int> AnsList;
int tot=0;
inline void Check(int k){
register int i,ans=0;
register ull h;
S.clear();
for(i=1;(i+k-1)<=n;i+=k){
h=Hash(i,k)*FilpHash(i,k);
if(!S.count(h)){
S.insert(h);
ans++;
}
}
if(ans>tot){
tot=ans;
AnsList.clear();
AnsList.push_back(k);
}else{
if(ans==tot)
AnsList.push_back(k);
}
}
int main(){
register int i,ans=0,maxv=0,cnt,temp;
bool flag;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%llu",&s[i]);
InitHash();
for(i=1;i<=n;i++){
Check(i);
}
printf("%d %d\n",tot,AnsList.size());
flag=false;
for(i=0;i<AnsList.size();i++){
if(flag)
putchar(' ');
flag=true;
printf("%d",AnsList[i]);
}
putchar('\n');
return 0;
}
[BZOJ 2118]墨墨的等式
论如何把数论乱搞和图论乱搞出在一起……
这个题由于要求[tex]x\ge 0[/tex],所以不能gcd乱搞。我们可以先取[tex]\{a_n\}[/tex]的最小值[tex]p[/tex](忽略为0的情况,为啥取最小值待会再说),对方程两边模[tex]p[/tex]。然后对于任何能使某个同余方程成立的[tex]\{x_n\}[/tex],将其中所有[tex]x_i[/tex]同时加任意个[tex]p[/tex],同余方程都成立。
取模后,[tex]B\in Z_p[/tex],所以说只要对于[tex]Z_p[/tex]中的每个数找出最小的一组合法解即能推出其他解(所以说,剩余系越少效率越高,这也就要求取的[tex]a_i[/tex]要小)。不过这个最小的一组合法解怎么找?
我们先找出任意一个合法[tex]B[/tex](比如说0吧),然后尝试加上[tex]a_i[/tex],就可以推出其他[tex]B\in Z_p[/tex]的最小解。这个应用当然是需要最短路辣。
求出来的最短路,表示的是取最小解时的[tex]B[/tex]。这样的话就可以推出某个前缀区间中合法[tex]B[/tex]的个数(能加多少[tex]p[/tex],就有多少个,注意不要忽略加0个的情况),并且答案符合区间减法,最后做差分即可。
注意忽略[tex]a_i=0[/tex]的情况(相当于不存在)。
代码:
/**************************************************************
Problem: 2118
User: danihao123
Language: C++
Result: Accepted
Time:1952 ms
Memory:5508 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#ifdef DEBUG
#include <cassert>
#endif
using namespace std;
typedef long long ll;
const int maxn=15;
const ll INF=0x7f7f7f7f7f7f7f7f;
ll A[maxn];
bool inQueue[500005];
ll d[500005];
int n;
ll minv;
inline void SPFA(){
register int i,u,to;
queue<int> Q;
memset(d,0x7f,sizeof(d));
d[0]=0;
Q.push(0);
inQueue[0]=true;
#ifdef DEBUG
assert(d[1]==INF);
#endif
while(!Q.empty()){
u=Q.front();
Q.pop();
inQueue[u]=false;
for(i=1;i<=n;i++){
to=(u+A[i])%minv;
if(d[u]<INF && d[u]+A[i]<d[to]){
d[to]=d[u]+A[i];
if(!inQueue[to]){
Q.push(to);
inQueue[to]=true;
}
}
}
}
}
inline ll Calc(ll x){
register ll ans=0;
register int i=0;
for(i=0;i<minv;i++)
if(d[i]<=x)
ans+=(x-d[i])/minv+1;
return ans;
}
int main(){
ll l,r;
register int i;
scanf("%d%lld%lld",&n,&l,&r);
minv=0x7fffffff;
for(i=1;i<=n;i++){
scanf("%d",&A[i]);
if(!A[i]){
i--;
n--;
continue;
}
minv=min(minv,A[i]);
}
SPFA();
printf("%lld\n",Calc(r)-Calc(l-1));
return 0;
}
[BZOJ 1711]吃饭
这道题当初竟然没A……so sad
这题的建图要这么来:吃的、喝的、牛什么的统统拉进图里(不过牛本身只能同时享受一种食物,所以说点容量为1,要拆成俩点)。对于吃的,从[tex]S[/tex]向每个吃的连一条边。喝的每个向[tex]T[/tex]连一条边。牛本身按照喜好连即可(注意要拆成俩点!)。
代码:
/**************************************************************
Problem: 1711
User: danihao123
Language: C++
Result: Accepted
Time:20 ms
Memory:864 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn=405,INF=0x7f7f7f7f;
namespace Dinic{
struct Edge{
int u,v,cap,flow;
};
vector<Edge> edges;
vector<int> G[maxn];
int m;
inline void AddEdge(int u,int v,int cap){
edges.push_back((Edge){u,v,cap,0});
edges.push_back((Edge){v,u,0,0});
m=edges.size();
G[u].push_back(m-2);
G[v].push_back(m-1);
}
bool vis[maxn];
int d[maxn];
int s,t;
bool BFS(){
register int i,x;
queue<int> Q;
memset(vis,0,sizeof(vis));
d[s]=0;
Q.push(s);
vis[s]=true;
while(!Q.empty()){
x=Q.front();
Q.pop();
for(i=0;i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(!vis[e.v] && e.cap>e.flow){
d[e.v]=d[x]+1;
Q.push(e.v);
vis[e.v]=true;
}
}
}
return vis[t];
}
int cur[maxn];
int DFS(int x,int a){
if(x==t || a==0)
return a;
int flow=0,f;
for(int& i=cur[x];i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(d[e.v]==d[x]+1){
f=DFS(e.v,min(a,e.cap-e.flow));
if(f>0){
flow+=f;
e.flow+=f;
edges[G[x][i]^1].flow-=f;
a-=f;
if(a==0)
break;
}
}
}
return flow;
}
inline int Maxflow(int S,int T){
register int ans=0;
s=S;
t=T;
while(BFS()){
memset(cur,0,sizeof(cur));
ans+=DFS(s,INF);
}
return ans;
}
};
int main(){
int N,F,D,f,d,x;
register int i,j,t;
scanf("%d%d%d",&N,&F,&D);
t=2*N+F+D+1;
for(i=1;i<=F;i++)
Dinic::AddEdge(0,i,1);
for(i=2*N+F+1;i<t;i++)
Dinic::AddEdge(i,t,1);
for(i=1;i<=N;i++)
Dinic::AddEdge(F+2*i-1,F+2*i,1);
for(i=1;i<=N;i++){
scanf("%d%d",&f,&d);
for(j=1;j<=f;j++){
scanf("%d",&x);
Dinic::AddEdge(x,2*i-1+F,1);
}
for(j=1;j<=d;j++){
scanf("%d",&x);
Dinic::AddEdge(2*i+F,F+2*N+x,1);
}
}
printf("%d\n",Dinic::Maxflow(0,t));
return 0;
}
[BZOJ 2190]仪仗队
这个题啊……有规律可循。
我们可以发现,关于答案[tex]a[/tex]有如下规律:
[tex]a+1=2\mul (\Sigma_{i=2}^{n-1}\varphi(i)+2)[/tex]
然后筛法求欧拉函数即可(我听说神犇们都用了杜教筛哎)
代码:
/**************************************************************
Problem: 2190
User: danihao123
Language: C++
Result: Accepted
Time:28 ms
Memory:976 kb
****************************************************************/
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=40005;
#define CUS_REP(i,a,b) for(i=(a);i<=(b);i++)
int phi[maxn];
int n;
void sieve(){
register int i,j;
phi[1]=1;
CUS_REP(i,2,n)
if(!phi[i])
for(j=i;j<=n;j+=i){
if(!phi[j])
phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
int main(){
register int i,ans=2;
scanf("%d",&n);
sieve();
/*
#ifdef DEBUG
CUS_REP(i,1,n)
printf("%d\n",phi[i]);
#endif
*/
CUS_REP(i,2,n-1)
ans+=phi[i];
printf("%d\n",ans*2-1);
return 0;
}