A 接竹竿
B 目录地址
分析: 很有意思的题目,记录下吧。当时读了前半段的题目,感觉不是很好写,读完输入输出,发现完全和前半部分没有关系, m次操作中,给定的就是一个线性的顺序,我们直接模拟就好了啊。
#include
using namespace std;
const int MAXN = 1e5 ;
vector<string>ve;
int main(){
int n,m;
while(cin>>n>>m){
ve.clear();
while(n--) { string a,b; cin>>a>>b; }
ve.push_back("C/");
while(m--){
string op; cin>>op;
if(op=="see"){
string ans=ve.back();
cout<<ans<<endl;
}else {
string x; cin>>x;
if(x!=".."){
x+="/";
string t=ve.back(); t+=x;
ve.push_back(t);
}else {
if(ve.size()==0) continue;
ve.pop_back();
}
}
}
}
return 0;
}
C逃出厂房
D疯狂精灵球
看懂题,模拟。
#include
#include
#include
using namespace std;
const int MAXN = 1e5 + 10;
set <int> s[MAXN];
int ma[MAXN];
int main()
{
int x,y,N,M,T;
scanf("%d",&T);
while(T--){
scanf("%d %d",&N,&M);
for(int i = 1; i <= MAXN; i++)
s[i].clear();
for(int i = 1 ; i <= N; i++)
scanf("%d",&ma[i]);
while(M--){
scanf("%d %d",&x,&y);
if(ma[x] == ma[y]) continue;
s[ma[x]].insert(ma[y]);
s[ma[y]].insert(ma[x]);
}
int cut = ma[1];
for(int i = 2 ; i <= N ; i++){
if(s[cut].size() < s[ma[i]].size())
cut = ma[i];
else if(s[cut].size() == s[ma[i]].size() && cut < ma[i])
cut = ma[i];
}
printf("%d\n",cut);
}
return 0;
}
E 文件统计
本想着是树链剖分,点权更新+路径求和。 其实不用,找一下规律就会发现,对于一个节点更新之后,只会影响其子树上的值,发现这一点之后就好弄了。
树状数组+dfs序
树状数组来控制 区间加减+单点查询
dfs序来维护以某个点为根的子树的 区间 。
代码
#include
using namespace std;
const int MAXN = 23333+11 ;
#define LL long long
struct BIT{
int n;
LL c[MAXN<<1];
inline int lowbit(int x){return x&(-x);}
void init(){ memset(c,0,sizeof(c)); }
void add(int x,int val){
for(int i=x;i<=n;i+=lowbit(i))
c[i]+=val;
}
LL sum(int x){
LL ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=c[i];
return ans;
}
}bit;
LL num[MAXN+2]; vector<int>ve[MAXN+2];
void init(int n){
bit.n=n; bit.init();
for(int i=0;i<=n;i++) ve[i].clear();
memset(num,0,sizeof(num));
}
int in[MAXN+2],out[MAXN+2],id;
void getmap(int m){
while(m--){
int a,b;
scanf("%d%d",&a,&b);
ve[a].push_back(b);
ve[b].push_back(a); // 汗TAT 有毒把,非要是双向边才可以。
}
}
void dfs(int now,int pre){
in[now]=++id;
for(int i=0;i<ve[now].size();i++){
int v=ve[now][i];
if(v!=pre) dfs(v,now);
}
out[now]=id;
}
int n,m;
void solve(int qq){
id=0; dfs(1,-1);
//for(int i=1;i<=n;i++){ printf("%d %d \n",in[i],out[i]); }
char op[10];int x;
while(qq--){
scanf("%s %d",op,&x);
if(op[0]=='q') printf("%lld\n",bit.sum(in[x]));
else{
int y; scanf("%d",&y);
if(op[0]=='a') {
num[x]+=y;
bit.add(in[x],y);
bit.add(out[x]+1,-y);
}else{
if(num[x]<y) puts("error");
else {
y=-y;
num[x]+=y;
bit.add(in[x],y);
bit.add(out[x]+1,-y);
}
}
}
}
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init(n);
getmap(n-1);
solve(m);
}
return 0;
}
F GCD与LCM
题目描述
Ocean某天遇到了一个很简单的数学题,但是他的大脑已经超负荷了。现在请你帮帮他吧:
已知D = GCD(x, y) + LCM(x, y),求合法组合(x, y)总数,其中x > 0且y > 0。
输入
第一行输入一个整数T,代表有T组测试数据。
接下来每行输入一个整数D。
注:1<=T,D<=1051<=T,D<=105。
输出
对每组测试数据,输出一个整数代表可能出现的组合数。
样例输入
3
2
3
4
样例输出
1
2
3
提示
GCD:最大公约数
LCM:最小公倍数
样例中:
若D=2,可能有①x=1,y=1;
若D=3,可能有①x=1,y=2②x=2,y=1
若D=4,可能有①x=1,y=3②x=2,y=2③x=3,y=1
分析: 今天想了许久,终于a掉。 其实最后化简的式子,和当时比赛时候化简的式子一样,不过有个地方没有弄好,结果当时怎么都过不了。
D = L + G 。
则 x = G * a . y = G * b .
x * y = G * G * a * b
x * y = G * L .
联立上式子: a * b = L / G = (D-G) / G = D/G -1
关键式子 : a * b = D / G -1 (注意这里D/G 一定是整数(因为L/G 一定是整数),同时a和b一定是互质的(如果不是互质的话, 就不能够满足G为最大公因数),比赛时就是这里没有想通 )
就是针对这个式子,开始化简,减少时间复杂度。
令 a * b = z .
z = D/ G - 1 .对于式子的右边值,我们可以遍历1-sqrt(D), 关键是 知道了a * b的值z,之前已经用了sqrt的时间复杂度,接下来的只能够用O(1)的了,我们怎么用o(1)处理 有多少对互质的数相乘为z呢? 这里想起来之前写的 一道题,可以o(n)预处理求出n以内任意数的 因子个数 ,感觉可以从这个筛法中取得 。
看代码吧 : 其实挺短的代码 。 时间复杂度 o(T*sqrt(D)) .学长说标程是o(T * log(D)) 的,目前还真是想不出来了。 标程出来了,再贴。
600ms
#include
using namespace std;
const int MAXN = 1e5+11;
#define LL long long
LL gcd(LL a,LL b){ return b==0?a:gcd(b,a%b); }
int num[MAXN+2];// num[i] 表示a*b=i的种类数
void init(){
num[1]=1;
for(LL i=1;i*i<=MAXN;i++){
for(LL j=i;j*i<=MAXN;j++){
if(i==j) continue;
if(gcd(i,j)==1) num[i*j]+=2; // 加上互质的条件
}
}
// for(int i=1;i<=20;i++){
// printf("i==%d %d\n",i, num[i]);
// }
}
LL solve(LL n){
LL ans=0;
for(LL i=1;i*i<=n;i++) { //sqrt(D) 枚举z
if(n%i==0){
//printf("i==%lld n=%lld \n",i,n);
ans+=num[n/i-1];
if(n/i!=i) ans+=num[i-1];
}
}
return ans;
}
int main(){
init();
int T;scanf("%d",&T);
while(T--){
LL n;scanf("%lld",&n);
if(n==1) puts("0");
else printf("%lld\n",solve(n));
}
return 0;
}
优化一下上述代码,在筛num[]的时候发现同时也可以将因子一并筛出来。
240ms
#include
using namespace std;
const int MAXN = 1e5+11;
#define LL long long
LL gcd(LL a,LL b){ return b==0?a:gcd(b,a%b); }
int num[MAXN+2];
int fac[MAXN+2][250],cnt[MAXN+2];
void init(){
num[1]=1; num[0]=0;
for(LL i=1;i*i<=MAXN;i++){// 这里可以同时筛选 两个
for(LL j=i;j*i<=MAXN;j++){
fac[i*j][cnt[i*j]++]=j;
if(i==j) continue;
if(gcd(i,j)==1) num[i*j]+=2;
}
}
}
LL solve(int n){
LL ans=0;
for(int i=0;i<cnt[n];i++){
int G=fac[n][i];
ans+=num[n/G-1];
if(n/G!=G) ans+=num[G-1];
}
return ans;
}
int main(){
init();
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
if(n==1) puts("0");
else printf("%lld\n",solve(n));
}
return 0;
}
分析二: 标程: 对于一个数字n来说,其可以拆分为a*b=n (a,b 互质)个数为 2^m(m为数n的质因子个数),从算数基本定理来考虑就行了。
一开始用了vector来存因子,然后枚举,400ms左右,后来换为普通数组模拟存因子,200ms左右 ,TAT ~
#include
using namespace std;
const int MAXN = 1e5+11;
#define LL long long
bool su[MAXN+2];int fac[MAXN+2][250],cnt[MAXN+2];int num[MAXN+2];
LL F[35]; // 数n的 质因子个数为Log(n)
void init(){ // 预处理都是o(n)
F[0]=1; for(LL i=1;i<=31;i++) F[i]=F[i-1]*2;
su[0]=su[1]=1;
for(int i=2;i<=MAXN;i++){
if(!su[i]) {
num[i]=1; 。
for(int j=2*i;j<=MAXN;j+=i){
su[j]=1;
num[j]++;
}
}
}
for(int i=1;i*i<=MAXN;i++){
for(int j=i;j*i<=MAXN;j++){
fac[i*j][cnt[i*j]++]=i;
if(i!=j) fac[i*j][cnt[i*j]++]=j;
//fac[j*i].push_back(j);
//if(i!=j) fac[i*j].push_back(i);
}
}
}
LL solve(int n){
LL ans=0;
for(int i=0;i<cnt[n];i++){
int t=n/fac[n][i]-1;
if(t>=1) ans+=F[num[t]];
}
return ans;
}
int main(){
init();
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
printf("%lld\n",solve(n));
}
return 0;
}
G: 杨八方的表面兄弟
分析: 很容易知道 ans = C(n,2)+C(n,3)+C(n,4)+C(n,n) .
由二项式定理容易知道 C(n,0)+C(n,1)+…C(n,n) = 2^n .
综上 ans = 2^n -1 -n .
最后取模这里 好多人容易错。
ans = ((2^n%mod) - 1 - n +mod ) % mod 。
百度的同余定理(a-b)%mod = (a%mod - b%mod )%mod ,当然是对的,但是对于做题来说,输出的都是>=0的值,为了防止最后值是负数,所以要先+mod 最后再mod掉, 这样就可以很好的避免。
H 杨八方的商业互吹
并查集随便写
#include
#include
using namespace std;
int a[1000020],par[1000020],ran[1000020],cnt[1000020];
int find(int m) {
if(m==par[m])
return m;
else
return par[m]=find(par[m]);
}
void unite(int x,int y) {
x=find(x);
y=find(y);
if(x==y)
return;
par[y]=x;
}
int main() {
int n,m;
scanf("%d %d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
}
for(int i=1; i<=n; i++) {
par[i]=i;
cnt[i]=1;
ran[i]=a[i];
}
while(m--) {
int x,y;
scanf("%d %d",&x,&y);
unite(x,y);
}
for(int i=1; i<=n; i++) {
int m=find(i);
if(m!=i) {
ran[m]+=a[i];
cnt[m]++;
cnt[i]=0;
}
}
double res=0;
for(int i=1; i<=n; i++) {
if(cnt[i]) {
res=max(res,1.0*ran[i]/cnt[i]);
}
}
printf("%.2lf\n",res);
return 0;
}
I 杨八方的编译原理
不难,当时没有时间写了。
模拟就好了。
#include
using namespace std;
#define LL long long
const int MAXN = 1e5;
const int MAXM = 1e6 ;
map<string,int>vis;
int getval(string s){ // 是否为变量,并获取其值。
bool flag=0; int ans=0;
for(int i=0;s[i];i++) {
if(s[i]<'0'||s[i]>'9') { flag =1; break;}
else
ans=ans*10+s[i]-'0';
}
if(flag) return vis[s];
return ans;
}
int main(){
int T ;cin>>T;
while(T--){
int ans; string s; cin>>s;
if(s=="show"){
string op; cin>>op;
ans=0; string t=""; string pre="+";
for(int i=0;op[i];i++){
if(op[i]!='-'&&op[i]!='+') t+=op[i];
else {
if(pre=="+") ans+=getval(t);
else if(pre=="-") ans-=getval(t);
pre=""; t=""; pre+=op[i];
}
}
if(pre=="+") ans+=getval(t);
else if(pre=="-") ans-=getval(t);
cout<<ans<<endl;
}else {
string t=""; string st=""; string pre="+";
for(int i=0;s[i];i++){
if(s[i]!='-'&&s[i]!='+'&&s[i]!='=') t+=s[i];
else{
if(s[i]=='=') { st=t; ans=0; }
else {
if(pre=="+") ans+=getval(t);
else if(pre=="-") ans-=getval(t);
pre=""; pre+=s[i];
}
t="";
}
}
if(pre=="+") ans+=getval(t);
else if(pre=="-") ans-=getval(t);
vis[st]=ans ;
}
}
return 0;
}
J 杨八方的英雄联盟
简单的二分,但是比赛的时候没有看 ,看到几何还有这么长的体面,没敢看下去,噗。真是~ 。
//package FirstTime;
import java.util.*;
import java.math.*;
import java.text.DecimalFormat;
public class Main{
static double R,H,a,b,c;
static double GetDis(double x1,double y1,double x2,double y2){
return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
static boolean f(double s){
return a*s*s+b*Math.sqrt(s)+c-H<0;
}
static void solve(double le,double ri) {// 求导后发现 恒为正,单调递增的。
double ans = -1.0;
while(ri-le>1e-8){
double mid = (le + ri) / 2;
if(f(mid)) le = mid ;
else {
ans = mid ; ri = mid ;
}
}
DecimalFormat df = new DecimalFormat("0.00");
if(ans==-1.0) System.out.println(-1);
else System.out.println(df.format(ans));
}
public static void main(String[] agrs){
Scanner cin = new Scanner(System.in);
int T;T=cin.nextInt();
while(T--!=0){
double x1,x2,y1,y2;
R=cin.nextDouble(); x1=cin.nextDouble(); y1=cin.nextDouble();
H=cin.nextDouble(); x2=cin.nextDouble(); y2=cin.nextDouble();
a=cin.nextDouble(); b=cin.nextDouble(); c=cin.nextDouble();
double d=GetDis(x1,y1,x2,y2);
double l=Math.abs(d-R),r=d+R;
solve(l,r);
}
}
}
K. 杨八方的绝地求生
签到
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main()
{
int t;
double v,x1,y1,r,x2,y2;
scanf("%d",&t);
while(t--)
{
scanf("%lf%lf%lf",&v,&x1,&y1);
scanf("%lf%lf%lf",&r,&x2,&y2);
double dis,a,b;
a=(x2-x1)*(x2-x1);
b=(y2-y1)*(y2-y1);
dis=sqrt(a+b);
if(dis<=r)
printf("0.0\n");
else
printf("%.1lf\n",(dis-r)*1.0/v);
}
return 0;
}
L. 杨八方的魔力占卜
签到
M: 得分期望
分析: 看题目意思,就是遍历序列,然后求当前值和其余值的异或值>=M的个数,看数据范围时间复杂度只能够为T * N * log(N) .
对于异或的处理,尤其是这种区间形式的异或,可以用01Trie ,01Trie的最典型的应用就是区间异或最大值,针对情况可能要用持久化的01Trie 。 这道题要进行一些变形 ,但是思想是一样的(从最高位贪心取值)。
看代码
在最基本的01Trie操作(插删查)的基础上进行简单的应用。
#include
using namespace std;
#define LL long long
const int MAXN = 10000+11;
const int MAXM = 1e6;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int ch[MAXN*32][2],sz;
int num[MAXN*32];
void init() {
memset(ch[0],0,sizeof(ch[0]));
sz=1;
memset(num,0,sizeof(num));
}
void Insert(int a) {
int u=0;
for(int i=31; i>=0; i--) {
int c=1&(a>>i);
if(!ch[u][c]) {
memset(ch[sz],0,sizeof(ch[sz]));
ch[u][c]=sz++;
}
u=ch[u][c];
num[u]++;
}
}
void Delete(int a) {
int u=0;
for(int i=31; i>=0; i--) {
int c=1&(a>>i);
u=ch[u][c];
num[u]--;
}
}
int Query(int a,int m) {//重点
int u=0;
int ans=0;
for(int i=31; i>=0; i--) { // 从最高位来枚举,贪心思想
int c=1&(a>>i); int d=1&(m>>i);
if(d==1){ //d=1的话,只能往c^1的方向走才能够保持最起码和m的值相等
if(ch[u][c^1]&&num[ch[u][c^1]]){
u=ch[u][c^1];
}else // 否则的话,c方向之后的值异或后肯定都小于m,可以返回ans了
return ans;
}else{// d=0
if(ch[u][c^1]&&num[ch[u][c^1]]){ // 如果c^1的方向可以走,那么这条路之后的数异或后肯定都大于m,直接加上就行。
ans+=num[ch[u][c^1]];
}
if(ch[u][c]&&num[ch[u][c]])// 如果c方向可以走,优先走这个方向,因为这条路上的值异或后才可能大于m
u=ch[u][c];
else // 如果c的方向不能够走,那么c^1这条路的值异或后肯定大于m,而且前面算过了,可以返回ans了
return ans;
}
}
ans+=num[u];//剩下加的都是异或后等于m的
return ans;
}
int arr[MAXN];
int main() {
int T; scanf("%d",&T);
while(T--){
init();
int n,m; scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) { scanf("%d",&arr[i]); Insert(arr[i]); }
for(int i=1;i<=n;i++){
Delete(arr[i]);
int ge=Query(arr[i],m);
// printf("ge = %d \n",ge);
Insert(arr[i]);
if(i>1) putchar(' ');
printf("%.2lf",ge*1.0/(n-1));
}
puts("");
}
return 0;
}