• A.直角三棱锥

  • 在三维空间中,平面 x = 0, y = 0, z = 0,以及平面 x + y + z = K 围成了一个三棱锥。整天与整数打交道的小明希望知道这个三棱锥内、上整点的数目。他觉得数量可能很多,所以答案需要对给定的 M 取模。

  • 答案为。注意mod的时候由于M不一定是质数,所以不能取逆元去做。

    
    #include"stdio.h" 
    long long a,b,c;
    void pan(){
    	long long i=0,j=0;
    	if (a%2==0&&i==0) a=a/2,i=1;
    	if (a%3==0&&j==0) a=a/3,j=1;
    	if (b%2==0&&i==0) b=b/2,i=1;
    	if (b%3==0&&j==0) b=b/3,j=1;
    	if (c%2==0&&i==0) c=c/2,i=1;
    	if (c%3==0&&j==0) c=c/3,j=1;
    }
    int main() {
    	long long i,j,k,n,t;
    	scanf("%lld",&t);
    	for (i=1;i<=t;i++) {
    		scanf("%lld %lld",&n,&k);
    		a=n+1;b=n+2;c=n+3;
    	    pan();
    	    long long ans=a%k*b%k*c%k;
    	    printf("%lld\n",ans);
    	}
    }
    

  • B.前缀查询

  • 有一个村子,和村民。维护下列四种操作:

    1.插入新人名 si,声望为 ai
    2.给定名字前缀 pi 的所有人的声望值变化 di
    3.查询名字为 sj 村民们的声望值的和(因为会有重名的)
    4.查询名字前缀为 pj 的声望值的和

  • 很明显的字典树了。别忘记每次pushdown的时候维护一下lazy标记。

    
    /*
    指针占内存大 
    所以要用数组字典树
    trie[maxn][27];表示下一个字母的位置
    sign[maxn]表示是否重复,val[maxn]表示前缀和
    */
    #include"stdio.h" 
    #include"string.h"
    #define maxn 1050005
    long long trie[maxn][27];
    long long number[maxn];//人数
    long long all[maxn];//声望和
    long long name[maxn];//当前人的声望和 
    long long numname[maxn];
    long long lazy[maxn];//标记 
    long long sum; 
    char s[1000005]; 
    void pushdown(long long p){
    	long long j;
    	if (lazy[p]==0) return;
    	 for (j=1;j<=26;j++) 
    		  if (trie[p][j]!=0){
    		  long long son=trie[p][j];
    		  lazy[son]+=lazy[p];
    		  all[son]+=number[son]*lazy[p];
    		  name[son]+=numname[son]*lazy[p];
    		}
    		lazy[p]=0;
    }
    
    void createtrie(char s[],long long x){
    	long long i,l,j;
    	long long p=0;
        long long id;
    	for (i=0;i

  • C.可达性

  • 给出一个 0 ≤ N ≤ 105 点数、0 ≤ M ≤ 105 边数的有向图,输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。

  • 强连通缩点之后,暴力判断某个回路的度。如果没有入度,那么在这个联通块上找一个最小的(以保证字典序最小)。

    给出一组不缩点bug的数据。

    4 4
    2 1
    2 3
    3 4
    4 2

    
    #include"stdio.h"
    #include"memory.h"
    struct node {
          int u,v,next;
      }edge[100050];
      int dfn[100050],low[100050],belong[100050];
      int stack[100050],head[100050],visit[100050],bcnt,cnt,tot,inde;
      int a[100050];
      int in[100050];
    void add(int x,int y){
    	cnt++;
    	edge[cnt].u=x;
    	edge[cnt].next=head[x];
    	edge[cnt].v=y;
    	head[x]=cnt;
    }
    int min(int a,int b){if (a>b) return b;else return a;}
    void tarjan(int x){
    	int i; 
    	tot++;
    	dfn[x]=tot;
    	low[x]=tot;
    	inde++;
    	stack[inde]=x;
    	visit[x]=1;
    	for (i=head[x];i!=-1;i=edge[i].next){
    		if (dfn[edge[i].v]==0){
    			tarjan(edge[i].v);
    			low[x]=min(low[x],low[edge[i].v]);
    		}
    		else if (visit[edge[i].v]!=0){
    			low[x]=min(low[x],dfn[edge[i].v]);
    		}
    		
    	}
    	if (low[x]==dfn[x]){
    	   bcnt++;
    	 do  {
    	//	printf("%d",stack[inde]);
    		visit[stack[inde]]=0;
    		belong[stack[inde]]=bcnt;
    		inde--;
    	} while  (x!=stack[inde+1]);
        }
    //	printf("\n");
    	return;
    }
    int main(){
    	int n,m;
    	int x,y;
    	int i;
    	int sum=0;
    	scanf("%d %d",&n,&m);
    	memset(head,-1,sizeof(head));
    	memset(visit,0,sizeof(visit));
    	memset(low,0,sizeof(low));
    	memset(dfn,0,sizeof(dfn));
    	 cnt=0;tot=0;inde=0;bcnt=0;sum=0;
         for(i=1;i<=m;i++)
         {
             scanf("%d %d",&x,&y);
             add(x,y);
         }
         for(i=1;i<=n;i++) if(dfn[i]==0)  tarjan(i);
         memset(in,0,sizeof(in));
         for (i=1;i<=cnt;i++) if (belong[edge[i].v]!=belong[edge[i].u]) in[belong[edge[i].v]]=1;
         int l;
    	 l=0;
         for (i=1;i<=n;i++) if (in[belong[i]]==0) {
         	l++;
         	a[l]=i;
         	in[belong[i]]=1;
         }
         printf("%d\n",l);
         for (i=1;i

    • D.codeJan和树

    • 给定一棵树,以1为根,树上的每条边都有距离。记每个点的值为从这个点出发,到所有在以这个点为根的子树中的点的距离之和。现在,给定一个m,你需要在树上找一个点,使得这个点的值减去以它为根的子树中的点的值<=m的最大值是多少。

    • 通过一遍树上dp,我们很容易求得每个点的值。然后,陷入沉思。如果对每个点都进行询问,然后去遍历它的子结点,显然,复杂度为O(n^2)。

    • 显然,这样的复杂度是完全不行的。那么,我们尝试使用数据结构去降低一下查询的复杂度。由于针对每一次查询的时候,sum[k]-sum[son]<=m中的sum[k]已经知道,我们需要求得最小的son,使得sum[k]-m<=sum[son]。在这里,我们可以应用一下划分树。也就是说,我们先用dfs序构造出来,然后放到划分树里。每次枚举结点的时候,可以找到对应的子树区间。然后对于子树区间,二分枚举第k大,然后划分树log找到值,去判断合不合法。复杂度约为O(n*log(n)^2)。

    • 显然,这么麻烦的方法,我根本懒得去实现。于是,看了题解。下面是题解介绍的方法,代码量短,思路简单,1A。

    • 在我们树上dp之后,可以发现,从每个结点,一直往上找,找到父亲结点,他们的值是递增的(这tm的不是废话)。所以,我们可以针对每一个结点,去询问它的所有父辈祖辈祖祖辈辈。倍增的方法,二分即可。复杂度为O(n*log(n))。

      
      #include"stdio.h"
      #include"string.h"
      #define maxn 105000 
      struct node{
      	long long v,dis,next;
      }edge[2*maxn];
      long long head[maxn];
      long long sum[maxn];
      long long number[maxn];
      long long sign[maxn][30];
      long long cnt;
      long long m;
      long long ans;
      long long max(long long a,long long b){
      	if (a>b) return a;return b;
      }
      void add(long long u,long long v,long long dis){
      	cnt++;
      	edge[cnt].v=v;
      	edge[cnt].dis=dis;
      	edge[cnt].next=head[u];
      	head[u]=cnt;
      }
      void dfs(long long k,long long father){
      	long long i,v;
      	number[k]=1;
      	sum[k]=0;
      	for (i=head[k];i!=-1;i=edge[i].next){
      		v=edge[i].v;
      		if (v!=father){
      			dfs(v,k);
      			number[k]+=number[v];
      			sum[k]+=sum[v]+number[v]*edge[i].dis;
      		}
      	}
      }
      void find(long long k,long long father){
      	long long i,v;
      	long long now,ff;
      	for (i=head[k];i!=-1;i=edge[i].next){
      		v=edge[i].v;
      		if (v!=father) {
      			sign[v][0]=v;
      			now=0;ff=k;
      			
      			while(ff!=-1) {
      				now++;
      				sign[v][now]=sign[ff][now-1];
      				ff=sign[ff][now-1];
      		    }
      		    find(v,k);
      		}
      	}
      }
      
      void query(long long k,long long father) {
      	long long i,v;
      	for (i=head[k];i!=-1;i=edge[i].next) {
      		v=edge[i].v;
      		if (v!=father){
      			query(v,k);
      		}
      	}
      	long long now=0;
      	long long ff=k;
      	int	j=1;
      		while(sign[ff][j]!=-1) {
      			now=sign[ff][j];
      			if (sum[now]-sum[k]<=m) ans=max(ans,sum[now]-sum[k]);
      			j++;
      			now=sign[ff][j];
      			if (sum[now]-sum[k]>m) {
      				ff=sign[ff][j-1];
      				j=1;
      				now=sign[ff][j];
      				if (now==-1||sum[now]-sum[k]>m) break; 
      			}
      		}
      	
      }
      int main(){
      	long long e,t,n,x,y,dis;
      	long long i,j;
      	scanf("%lld",&t);
      	for (e=1;e<=t;e++) {
      		cnt=0;
      		memset(head,-1,sizeof(head));
      		scanf("%lld %lld",&n,&m);
      		for (i=1;i

  • E.无效位置

  • 给一个1-base数组{a},有N次操作,每次操作会使一个位置无效。一个区间的权值定义为这个区间里选出一些数的异或和的最大值。求在每次操作前,所有不包含无效位置的区间的权值的最大值。

  • 只要知道线性基这个东西,就应该能写过此题吧。

    线性基大概就是,像网络流类比水流一样,可以把异或中的线性基类比为n维向量空间的一组基。n维向量空间中,可以找到一族基向量,比如单位基,可以去表示空间中的所有向量。我们可以发现,如果我们在异或基中,取2进制的1,10,100,1000,10000......这样子取,空间中的任何数字都能被这组基所表示出来。当然,n为向量空间中的一组基不一定要是单位基,只要n个向量线性无关即可。所以,我们在取线性基的时候,同样保证线性无关即可,即基中的任意一个数不能通过其他的数异或得到。

  • 由于数字最大为1e9,不超过32位整数。所以最多会产生32个基,于是我们只需要开[1e5*32]的空间即可。然后,倒着开始做。每次用并查集来维护区间,将这个点和左右区间相连。然后线性基暴力合并,查找一下最大值。

    
    #include"stdio.h"
    #include"string.h"
    #include"algorithm"
    using namespace std;
    long long sign[105000][40];
    long long b[105000];
    long long vis[105000];
    long long a[105000];
    long long wei[105000];
    long long father[105000];
    void insert(long long k,long long wei){
    	long long i;
    	for (i=40;i>=1;i--) if ((k&(1LL<<(i-1)))>0){
    		if (sign[wei][i]>0) k=k^sign[wei][i];
    		else {
    			sign[wei][i]=k;
    			return;
    		}
    	}
    }
    long long getfather(long long k){
    	if (father[k]==k) return k;
    	else father[k]=getfather(father[k]);
    	return father[k];
    }
    long long find(long long wei){
    	long long i;
    	long long ans=0;
    	for (i=40;i>=1;i--)if (sign[wei][i]>0) 
    		if ((ans^sign[wei][i])>ans) ans=(ans^sign[wei][i]);
    	
    	return ans;
    }
    
    int main(){
    	long long ans,n,i,j,x,y;
    	long long l=0;
    	scanf("%lld",&n);
    	for (i=1;i<=n;i++) {scanf("%lld",&a[i]);insert(a[i],i);}
    	for (i=1;i<=n;i++) scanf("%lld",&wei[i]);
    	for (i=1;i<=n;i++) father[i]=i;
        memset(vis,0,sizeof(vis));
        ans=0;
    	for (i=n;i>=1;i--){
        	if (vis[wei[i]-1]==1) {
        		x=getfather(wei[i]-1);
        		y=getfather(wei[i]);
        		father[y]=x;
        		for (j=1;j<=40;j++) if (sign[y][j]>0){
        			insert(sign[y][j],x);
        		} 
        	}
        	if (vis[wei[i]+1]==1) {
        		x=getfather(wei[i]);
        		y=getfather(wei[i]+1);
        		father[y]=x;
        		for (j=1;j<=40;j++) if (sign[y][j]>0){
        			insert(sign[y][j],x);	
        		}
        	}
        	x=getfather(wei[i]);
        	ans=max(find(x),ans);
        	l++;
        	b[l]=ans;
        	vis[wei[i]]=1;
        }
        for (i=l;i>=1;i--) printf("%lld\n",b[i]);
        
    }
    
优质内容筛选与推荐>>
1、Android 体系结构和应用程序组成
2、《JAVA程序设计》_第十周学习总结
3、android游戏开发系列(1)——迅雷不及掩耳的声音
4、术语抽取的程序(计算机专业术语的抽取 )java代写
5、Oracle SQLPlus的一个低级错误


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号