ACdream 1210 Chinese Girls' Amusement (规律+数学)
【题意】:
求最大的k<=n/2使得gcd(n,k)=1。
/*
找到与N互质的最大整数K即可。当N为奇数时,(int)(N/2)即为所求数;当N为偶数时,
如果N/2 - 1是奇数,则为所求结果,如果为偶数,N/2 - 2为所求结果
*/
java代码:
//感觉就是暴力 从n/2 开始减 ,直到互素为止,
import java.io.*;
import java.util.*;
import java.math.BigInteger;//声明BigInteger大数类
import java.lang.*;
public class Main
{
public static void main(String args[])
{
Scanner cin = new Scanner(System.in);
BigInteger n,k;
n=cin.nextBigInteger();
k=n.divide(new BigInteger("2"));
while(n.gcd(k).compareTo(BigInteger.ONE)!=0)
{
k=k.subtract(BigInteger.ONE);
}
System.out.println(k);
}
}
C++代码:
/*
* this code is made by herongwei
* Problem: 1210
* Verdict: Accepted
* Submission Date: 2015-10-11 10:07:54
* Time: 0MS
* Memory: 1672KB
*/
#include <bits/stdc++.h>
using namespace std;
string ss,ans;
template <class T>
void to_string(string &result,const T&t)
{
ostringstream oss;//创建一个流
oss<<t;//把值传入流中
result=oss.str();
}
void divtwo(const string& ss)// div two
{
ans="";
int len=ss.length(),tp;
tp=ss[0]-'0';
if(tp>1) ans+=char (tp/2+'0');
tp&=1;
for(int i=1; i<len; ++i){
tp=tp*10+ss[i]-'0';
ans+=char(tp/2+'0');
tp&=1;
}
}
void sub()//sub 1
{
int i=ans.length()-1;
while(ans[i]=='0'){
ans[i]='9';
--i;
}
ans[i]-=1;
}
void print()
{
int i=0;
//while(ans[i]=='0') ++i; //qian dao 0
for(; i<ans.length(); ++i)
cout<<ans[i];
cout<<endl;
}
int main()
{
cin>>ss;
divtwo(ss);
if((ss[ss.length()-1]-'0')&1) cout<<ans<<endl;
else{
sub();
if((ans[ans.length()-1]-'0')&1){
print();
}
else{
sub();
print();
}
}
return 0;
}
ACdream 1211 Reactor Cooling【上下界网络流 + 输出流量】
题意:
给n个点,及m根pipe,每根pipe用来流躺液体的,单向的,每时每刻每根pipe流进来的物质要等于流出去的物质,要使得m条pipe组成一个循环体,里面流躺物质。
并且满足每根pipe一定的流量限制,范围为[Li,Ri].即要满足每时刻流进来的不能超过Ri(最大流问题),同时最小不能低于Li。
例如:
46(4个点,6个pipe)
12 1 3 (1->2上界为3,下界为1)
23 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3
可行流:

再如:所有pipe的上界为2下界为1的话,就不能得到一种可行流。
题解:
上界用ci表示,下界用bi表示。
下界是必须流满的,那么对于每一条边,去掉下界后,其自由流为ci– bi。
主要思想:每一个点流进来的流=流出去的流
对于每一个点i,令
Mi= sum(i点所有流进来的下界流)– sum(i点所有流出去的下界流)
如果Mi大于0,代表此点必须还要流出去Mi的自由流,那么我们从源点连一条Mi的边到该点。
如果Mi小于0,代表此点必须还要流进来Mi的自由流,那么我们从该点连一条Mi的边到汇点。
如果求S->T的最大流,看是否满流(S的相邻边都流满)。
满流则有解,否则无解。

代码:
/*
*上下界的网络流,用上界减去下界
* Problem: ACdream 1211
* Running time: 16MS
* Complier: G++
* Author: herongwei
* Create Time: 19:59 2015/10/8 星期四
*/
#include <stdio.h>
#include <string.h>
#include <vector>
#include <queue>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=250;
const int inf=0x3f3f3f3f;
#define clc(a,b) memset(a,b,sizeof(a));
struct node1
{
int fm,to,cap,flow;
};
vector <int> v[N];
vector <node1> e;
int vis[N],cnt[N];
void add_edge(int fm,int to,int cap)
{
e.push_back((node1){fm,to,cap,0});
e.push_back((node1){to,fm,0,0});
int tmp=e.size();
v[fm].push_back(tmp-2);
v[to].push_back(tmp-1);
}
bool bfs(int s,int t)
{
clc(vis,-1);
queue<int> q;
q.push(s);
vis[s] = 0;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<v[x].size();i++)
{
node1 tmp = e[v[x][i]];
if(vis[tmp.to]<0 && tmp.cap>tmp.flow) //第二个条件保证
{
vis[tmp.to]=vis[x]+1;
q.push(tmp.to);
}
}
}
if(vis[t]>0)return true;
return false;
}
int dfs(int o,int f,int t)
{
if(o==t || f==0) //优化
return f;
int a = 0,ans=0;
for(int &i=cnt[o];i<v[o].size();i++)
{
node1 &tmp = e[v[o][i]];
if(vis[tmp.to]==(vis[o]+1) && (a = dfs(tmp.to,min(f,tmp.cap-tmp.flow),t))>0)
{
tmp.flow+=a;
e[v[o][i]^1].flow-=a; //存图方式
ans+=a;
f-=a;
if(f==0) //注意优化
break;
}
}
return ans; //优化
}
int dinic(int s,int t)
{
int ans=0;
while(bfs(s,t))
{
clc(cnt,0);
int f=dfs(s,inf,t);
ans+=f;
}
return ans;
}
int come[N],to[N];
int flow[N][N];
struct node2
{
int x,y;
} num[N*N];
void init(int n)
{
for(int i=0;i<=n;i++)
v[i].clear();
e.clear();
}
int main()
{
//freopen("1.txt","r",stdin);
int _,__;
while(~scanf("%d%d",&_,&__))
{
clc(come,0);
clc(to,0);
clc(flow,0);
int s=0,t = _+1;
for(int i=0;i<__;i++)
{
int x,y,mi,ma;
scanf("%d%d%d%d",&x,&y,&mi,&ma);
num[i] = (node2){x,y};
flow[x][y] += mi;
add_edge(x,y,ma-mi);
come[x]+= mi;
to[y] += mi;
}
int s2=0;
for(int i=1;i<=_;i++)
{
int tmp = come[i]-to[i];
if(tmp<0)
{
s2+=(-tmp);
add_edge(s,i,-tmp);
}
if(tmp>0) add_edge(i,t,tmp);
}
int ans = dinic(s,t);
if(ans != s2)
puts("NO");
else
{
puts("YES");
for(int i=1;i<=_;i++)
{
for(int j=0;j<v[i].size();j++)
{
int f = v[i][j];
flow[e[f].fm][e[f].to]+=abs(e[f].flow);
}
}
for(int i=0;i<__;i++)
{
printf("%d\n",flow[num[i].x][num[i].y]);
}
}
init(_);
} return 0;
}
/*
Sample Input
4 6
1 2 1 2
2 3 1 2
3 4 1 2
4 1 1 2
1 3 1 2
4 2 1 2
4 6
1 2 1 3
2 3 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3
Sample Output
NO
YES
1
2
3
2
1
1
*/
ACdream 1213 Matrix Multiplication (数学)
【题意】:
A是图G的关联矩阵,求B=ATA的元素和。
其实就是统计每个数的个数,最后平方求和
代码:
/*
* this code is made by herongwei
* Problem: 1213
* Verdict: Accepted
* Submission Date: 2015-10-06 21:11:02
* Time: 76MS
* Memory: 1712KB
*/
#include <bits/stdc++.h>
using namespace std;
int arr[10005];
int main()
{
int n,m;cin>>n>>m;
memset(arr,0,sizeof(arr));
for(int i=0; i<m; ++i)
{
int u,v;
cin>>u>>v;
arr[u]++;
arr[v]++;
}
int s=0;
for(int i=1; i<=n; ++i)
s+=arr[i]*arr[i];
cout<<s<<endl;
}
ACdream 1216 Beautiful People(二路单调LIS)
【题意】:
选取最多的人,要求不存在两个人i和j,Si <= Sj && Bi >= Bj。
【思路】先把所有人按S升序排序,然后再按B降序排序,对B求最长上升子序列(LIS)。LIS是很经典的问题了,简单的DP复杂度问O(n^2),这里需要使用经典的O(nlg(n))的算法。
代码:
/*
* this code is made by herongwei
* Problem: 1216
* Verdict: Accepted
* Submission Date: 2015-10-08 20:39:41
* Time: 132MS
* Memory: 3632KB
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
struct node
{
int x,y,id;
bool operator <(const node &t) const
{
if(x!=t.x) return x<t.x;
if(y!=t.y) return y>t.y;
}
} st[maxn];
int dp[maxn];//dp[i]表示最长上升子序列长度为i时子序列末尾的最小B值
int LIS[maxn];//LIS[i]表示以第i个people结尾的最长上升子序列的长度
int main()
{
//freopen("1.txt","r",stdin);
int _;
while(~scanf("%d",&_))
{
memset(dp,inf,sizeof(dp));
memset(LIS,0,sizeof(LIS));
for(int i=1; i<=_; ++i)
{
scanf("%d%d",&st[i].x,&st[i].y);
st[i].id=i;
}
sort(st+1,st+_+1);
int len=1,ans=1;
for(int i=1; i<=_; ++i)
{
int ck=lower_bound(dp+1,dp+len+1,st[i].y)-dp;//返回大于或等于val的第一个元素位置;
if(ck==len) len++;
dp[ck]=st[i].y;
LIS[i]=ck;
ans=max(ans,ck);
// cout<<"ck= "<<ck<<endl;
// cout<<"i= "<<i<<"st[i].x= "<<st[i].x<<"st[i].y= "<<st[i].y<<endl;
// cout<<"--------------"<<endl;
}
// cout<<"len= "<<len<<endl;
printf("%d\n",ans);
for(int i=_; i>=1; --i)
{
if(LIS[i]==ans)
{
printf("%d",st[i].id);
if(len!=1) printf(" ");
ans--;
}
}
puts("");
}
return 0;
}
ACdream 1427 Nice Sequence
【题意】:
Time Limit:
4000/2000MS (Java/Others)
Memory Limit:
128000/64000KB (Java/Others)
Submit Status
Problem Description
Let us consider the sequence a1, a2,..., an of non-negative integer numbers. Denote as ci,j the number of occurrences of the number i among a1,a2,..., aj. We call the sequence k-nice if for all i1<i2 and for all j the following condition is satisfied: ci1,j ≥ ci2,j −k.
Given the sequence a1,a2,..., an and the number k, find its longest prefix that is k-nice.
Input
The first line of the input file contains n and k (1 ≤ n ≤ 200 000, 0 ≤ k ≤ 200 000). The second line contains n integer numbers ranging from 0 to n.
Output
Output the greatest
l such that the sequence a
1, a
2,..., a
l is k-nice.
Sample Input
10 1
0 1 1 0 2 2 1 2 2 3
2 0
1 0
Sample Output
【题意】这道题理解题意很重要,
其实就是当我们从左向右输入一个元素x时,要保证其左侧元素中0-(x-1)出现的次数大于等于 x的出现次数-k,找出这样的最长的前缀。
【思路】:每次输入一个元素,要更新它的次数,还要比较前(x-1)个出现次数的最小值,因此可用线段树维护最小值。只要判断当前x的次数和(前x-1)的次数最小值比较小于K,则break,否则输出查找的最长前缀。
代码:
/*
* 线段树 单点更新,单点查询
* Problem: ACdream No.1427
* Running time: 252MS
* Complier: G++
* Author: javaherongwei
* Create Time: 21:00 2015/10/7
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=200010;
const int inf=0x3f3f3f3f;
inline int max(int a,int b) {return a>b?a:b;}
inline int min(int a,int b) {return a<b?a:b;}
struct node
{
int left,right,minv,maxv;
int mid(){
return (left+right)>>1;
}
}tree[maxn<<2];
int n,k,sum[maxn<<2];
int arr[maxn],A[maxn];
void build(int l,int r,int rt)
{
tree[rt].left=l;
tree[rt].right=r;
tree[rt].minv=tree[rt].maxv=0;
if(l==r) return ;
int m=tree[rt].mid();
build(l,m,rt*2);
build(m+1,r,rt*2+1);
}
void update(int pos,int add,int rt)
{
int l=tree[rt].left;
int r=tree[rt].right;
if(l==r)
{
tree[rt].minv+=add;
return ;
}
int m=tree[rt].mid();
if(m>=pos) update(pos,add,rt*2);
else update(pos,add,rt*2+1);
tree[rt].minv=min(tree[rt*2].minv,tree[rt*2+1].minv);
}
int query_min(int ql,int qr,int rt)//query(0,arr[ii]-1,1)
{
int l=tree[rt].left;
int r=tree[rt].right;
if(ql<=l&&r<=qr)
{
return tree[rt].minv;
}
int m=tree[rt].mid();
int ans1,ans2;
if(m>=qr) return query_min(ql,qr,rt<<1);
else if(m<ql) return query_min(ql,qr,rt<<1|1);
return min(query_min(ql,qr,rt<<1),query_min(ql,qr,rt<<1|1));
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(arr,0,sizeof(arr));memset(A,0,sizeof(A));
for(int i=1; i<=n; ++i) scanf("%d",&arr[i]);
build(0,n,1);
int ii;
for(ii=1; ii<=n; ++ii)
{
A[arr[ii]]++;
update(arr[ii],1,1);
if(arr[ii]==0) continue;
int ans=query_min(0,arr[ii]-1,1);
if(ans<A[arr[ii]]-k) break;
}
printf("%d\n",ii-1);
} return 0;
}
/*
10 1
0 1 1 0 2 2 1 2 2 3
2 0
1 0
Sample Output
8
0
*/<span style="font-family:Arial, Helvetica, sans-serif;">
</span>
ACdream 1431
Sum vs Product
【题意】:求固定长度为n(2=<n<=500)的且满足集合内所有元素之和恰好等于所有元素之积的集合的个数
【思路】找出前10个,发现规律:对于n(n>2),必然出现这么一个集合{1,1,1,,,,2*n}内
如:
n
3 {1,2,3}
4 {1,1,2,4}
5 {1,1,1,2,5} {1,1,2,2,2} {1,1,1,3,3}
。。。
集合最大元素必然不超过n,因此后面的元素从n依次递减到2,因此要满足条件,必须要1来补充,发现假如全部填2,则最多填2^9=512>500,即9个2
对于数据范围,可以考虑暴搜
代码:
/*
* DFS
* Problem: ACdrema No.1431
* Running time: 0MS
* Complier: G++
* Author: javaherongwei
* Create Time: 21:00 2015/10/7
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n,sum;
// 当前可枚举的最大值,深度,当前积,当前和
//剪枝:当前积小于当前和加当前长度则继续搜索
void dfs(int cur,int now_ji,int now_he,int depth){
for(int i=cur; i>=2; --i){
if(depth>n) break;
if(now_ji*i==(now_he+i+n-depth)) sum++;
else if(now_ji*i<(now_he+i+n-depth))
dfs(i,now_ji*i,now_he+i,depth+1);
}
}
int main(){
while(~scanf("%d",&n)){
sum=0;
dfs(n,1,0,1);
printf("%d\n",sum);
}
return 0;
}
详细题解:
http://blog.watashi.ws/623/andrew-stankevich-1-solutio/