比较裸的$exgcd$的应用?

$exgcd$可以算出在$x$和$y$分别是最小正整数时的解。注意在这里因为有$a(x+\frac{b}{d})+b(y-\frac{a}{d})=c$,$d=gcd(a,b)$,所以$\frac{b}{d}$和$\frac{a}{d}$一定是整数,所以最小$x$的整数解%的应该是$\frac{b}{d}$而不是$b$,$y$同理。

算出两种情况的最小整数解后,$x$为最小正数时$x$一直在加,$y$一直在减,所以$x$最小时$y$都为非正数,则无解。判$y$时同理。这两种情况都排除后算出的$x$和$y$相当于是对应的最大$x$,$y$和最小$x$,$y$。算答案就用$x$大的减小的再除以$\frac{b}{d}$算得的就是其中一共有多少解。

最后就是要先判掉各种0的情况,避免$exgcd$中整除0或者$mod$0。

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std; void exgcd ( LL a, LL b, LL &x, LL &y, LL &d ) {
if ( b == ) {
d = a; x = ; y = ;
return ;
}
LL x0, y0;
exgcd ( b, a % b, x0, y0, d );
x = y0;
y = x0 - ( a / b ) * y0;
} int main ( ) {
freopen ( "fuction.in", "r", stdin );
freopen ( "fuction.out", "w", stdout );
int T;
scanf ( "%d", &T );
while ( T -- ) {
LL a, b, c;
scanf ( "%I64d%I64d%I64d", &a, &b, &c );
if ( !a && !b && c ) { puts ( "" ); continue; }
if ( !a && !b && !c ) { puts("ZenMeZheMeDuo"); continue; }
if ( !a ) {
if ( c % b != || c / b < ) printf ( "0\n" );
else puts("ZenMeZheMeDuo");
continue;
} else if ( !b ) {
if ( c % a != || c / a < ) printf ( "0\n" );
else puts("ZenMeZheMeDuo");
continue;
}
if ( a < ) a = -a, b = -b, c = -c;
LL x, y, d;
exgcd ( a, b, x, y, d );
if ( c % d ) printf ( "0\n" );
else if ( b < ) { printf ( "ZenMeZheMeDuo\n" ); continue; }
else {
x *= c / d; y *= c / d; LL numy;
LL x1 = ( x % ( b / d ) + ( b / d ) ) % ( b / d );
if ( x1 == ) x1 += b / d;
LL y1 = ( c - x1 * a ) / b;
LL y2 = ( y % ( a / d ) + ( a / d ) ) % ( a / d );
if ( y2 == ) y2 += a / d;
LL x2 = ( c - y2 * b ) / a;
if ( y1 <= || x2 <= ) printf ( "0\n" );
else if ( ( x2 - x1 ) / ( b / d ) + > ) printf ( "ZenMeZheMeDuo\n" );
else printf ( "%I64d\n", ( x2 - x1 ) / ( b / d ) + );
}
}
return ;
}

一道以前就做过的经典树规+背包的题目。定义$dp[u][k]$表示$u$这棵子树内选择$k$个黑点能对答案产生的最大贡献。所以每次遍历子树时用背包的思想来计算当前连接到子树的边的贡献,更新子树的答案即可。

注意这里的初始化,最开始要把没有经过的状态定成-1,避免更新不存在的情况。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define LL long long
using namespace std; int n, m; void read ( int & x ) {
x = ; char ch = getchar ( );
while ( ch > '' || ch < '' ) ch = getchar ( );
while ( ch >= '' && ch <= '' ) {
x = x * + ch - ''; ch = getchar ( );
}
} struct Node {
int v, nex; LL w;
Node ( int v = , LL w = , int nex = ) :
v ( v ), w ( w ), nex ( nex ) { }
} Edge[]; int h[], stot;
void add ( int u, int v, LL s ) {
Edge[++stot] = Node ( v, s, h[u] );
h[u] = stot;
} int siz[];
LL dp[][];
void Dfs ( int u, int f ) {
siz[u] = ;
memset ( dp[u], -, sizeof ( dp[u] ) );
dp[u][] = , dp[u][] = ;
for ( RG int i = h[u]; i; i = Edge[i].nex ) {
int v = Edge[i].v;
if ( v == f ) continue;
Dfs ( v, u );
siz[u] += siz[v];
for ( RG int j = min ( siz[u], m ); j >= ; j -- )
for ( RG int k = ; k <= min ( siz[v], j ); k ++ ) {
LL tmp = 1LL * Edge[i].w * ( 1LL * k * ( m - k ) + 1LL * ( siz[v] - k ) * ( n - m - siz[v] + k ) );
tmp += dp[v][k];
if ( dp[u][j-k] != - ) dp[u][j] = max ( dp[u][j], dp[u][j-k] + tmp );
}
} } int main ( ) {
freopen ( "coloration.in", "r", stdin );
freopen ( "coloration.out", "w", stdout );
scanf ( "%d%d", &n, &m );
for ( int i = ; i < n; i ++ ) {
int u, v, s;
read ( u ); read ( v ); read ( s );
add ( u, v, s );
add ( v, u, s );
}
Dfs ( , );
printf ( "%I64d", dp[][m] );
return ;
}

把方向搞错了以至于现在不是很想写!!!

以下粘的题解:

光线只有遇上边界或堵塞的格子才会改变方向,所以改变方向的位置是有限的,光线的方向又最多只有四种,所以光线在循环之前改变方向的次数是$O(n+m+k)$级别的。我们可以模拟光线的移动。已知光线位置和光线的方向,使用二分的方法可以在$O(log k)$的时间复杂度内求出即将改变方向的位置和改变后的方向。

我们暂把光线的位置和方向称为光线的状态。一种状态能转移到一种且仅有一种状态。如果从状态$a$能转移到状态$b$,那么$b$反向后的状态能转移到$a$反向后的状态。所以一种状态能从一种且仅有一种状态转移而来。这就像是一种置换,所以从初始状态出发,必定会回到初始状态,并且回到初始状态之前不会重复经过某种状态。

我们对网格进行染色,有邻边的格子颜色不同,形成一个二分图。根据题目中光线反射的方式,可以发现,每当光线沿西北、东南方向前进时,只会经过一种颜色的网格,每当光线沿东北、西南方向前进时,只会经过另一种颜色的网格。所以光线在某一个格子中心时,要么只会是西北、东南方向之一,要么只会是东北、西南方向之一。

这样,如果一次循环内一个格子被重复经过,只有可能是光线以相反的两个方向进入,并且一次循环内一个格子最多被经过两次。一个格子被经过两次,所有被光线经过的格子都会被经过两次。易知,如果光线在前进过程中出现过如下两种反射,所有格子就会被经过两次。只需在模拟的过程中记录是否出现过这两种情况即可。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N=;
int n,m,k,dx,dy,x,y;
long long ans;
bool f;
char ch[];
struct qwe
{
int x,y;
qwe(int X=,int Y=)
{
x=X,y=Y;
}
bool operator < (const qwe &b) const
{
return x<b.x||(x==b.x&&y<b.y);
}
};
vector<qwe>a[];
int read()
{
int r=,f=;
char p=getchar();
while(p>''||p<'')
{
if(p=='-')
f=-;
p=getchar();
}
while(p>=''&&p<='')
{
r=r*+p-;
p=getchar();
}
return r*f;
}
void add(int x,int y)
{
a[].push_back(qwe(x-y,x));
a[].push_back(qwe(x+y,x));
}
void wk()
{
int fl=(dx!=dy);
qwe p=fl?qwe(x+y,x):qwe(x-y,x);
vector<qwe>::iterator it=upper_bound(a[fl].begin(),a[fl].end(),p);
for(;it->x!=p.x;it--);
if(dx<)
for(;it->y>=x;it--);
ans+=abs(x-it->y)-;
x=it->y,y=fl?it->x-x:x-it->x;
bool u=binary_search(a[].begin(),a[].end(),qwe(x-y-dx,x-dx)),v=binary_search(a[].begin(),a[].end(),qwe(x-y+dy,x));
if(u==v)
f=,dx*=-,dy*=-;
else if(u)
x-=dx,dy*=-;
else if(v)
y-=dy,dx*=-;
}
int main()
{
freopen("ray.in","r",stdin);
freopen("ray.out","w",stdout);
n=read(),m=read(),k=read();
for(int i=;i<=m;i++)
add(,i),add(n+,i);
for(int i=;i<=n+;i++)
add(i,),add(i,m+);
for(int i=;i<=k;i++)
{
int x=read(),y=read();
add(x,y);
}
x=read(),y=read();
scanf("%s",ch);
dx=(ch[]=='N')?-:,dy=(ch[]=='W')?-:;
sort(a[].begin(),a[].end());
sort(a[].begin(),a[].end());
wk();
int sx=x,sy=y,sdx=dx,sdy=dy;
ans=;
do
wk();
while(!(x==sx&&y==sy&&dx==sdx&&dy==sdy));
printf("%I64d\n",(f)?(ans>>):ans);
return ;
}

【9.2校内测试】【开学祭】【exgcd】【树规(背包】【模拟】的更多相关文章

  1. 【10.29校内测试】【线段树】【DP】【二进制Trie树求最小值最大】

    Solution 标程太暴力惹QAQ 相当于是26棵线段树的说QAQ 不过我写了另一种写法,从大到小枚举每一个字母,标记字典序在这个字母之上的位置为1,每次都建一棵线段树,维护1的数量,即区间和. 修 ...

  2. 【10.10校内测试】【线段树维护第k小+删除】【lca+主席树维护前驱后驱】

    贪心思想.将a排序后,对于每一个a,找到对应的删除m个后最小的b,每次更新答案即可. 如何删除才是合法并且最优的?首先,对于排了序的a,第$i$个那么之前就应该删除前$i-1$个a对应的b.剩下$m- ...

  3. 【8.30校内测试】【找规律模拟】【DP】【二分+贪心】

    对于和规律或者数学有关的题真的束手无策啊QAQ 首先发现两个性质: 1.不管中间怎么碰撞,所有蚂蚁的相对位置不会改变,即后面的蚂蚁不会超过前面的蚂蚁或者落后更后面的蚂蚁. 2.因为所有蚂蚁速度一样,不 ...

  4. POJ 3345-Bribing FIPA(树状背包)

    题意: 有n个国家投票,要得到一个国家的投票有一定的花费,如果给到一个国家的票同时也得到了它所有附属国的票,给出国家关系树,求至少得到m票的最小花费. 分析:基础树状背包,dp[i][j],以i为根的 ...

  5. [HEOI2015]兔子与樱花 树规+贪心

    鬼能想到是个贪心.明明觉得是树规啊..又完美爆零.. 从叶子节点往上更新,能保证最优解(这块想了半天). 证明:当你的子树上有能删的点而你不删时,可能会对子树的根节点有利,最好的情况是使子树根节点由不 ...

  6. 【LibreOJ】#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop 线段树+完全背包

    [题目]#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop [题意]给定一个长度为n的物品序列,每个物品有价值.不超过m的重量.要求支持以下三种操作:1.物品价值区间加减,2.物 ...

  7. Codechef Black Nodes in Subgraphs(树型背包)

    题目链接 Black Nodes in Subgraphs 题目意思就是在一棵树中所有点标记为两种颜色(黑和白) 然后询问是否存在大小为X恰好有Y个黑点的连通块 这题我们可以用树型背包的方法 设$f[ ...

  8. HDU 1561:The more, The Better(有依赖的树型背包)

    http://acm.hdu.edu.cn/showproblem.php?pid=1561 题意:有n个点,容量为m,每个点有一个价值,还给出n条边,代表选第i个点之前必须先选ai,问最多的价值能取 ...

  9. 【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】

    比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; ...

随机推荐

  1. Oracle解锁scott账户

    Oracle安装完成之后scott账户默认是锁定的,登录的时候会提示账户已经被锁定: C:\Users\CC11001100>sqlplus scott/toor SQL*Plus: Relea ...

  2. 可怕的npm蠕虫

    https://hackernoon.com/im-harvesting-credit-card-numbers-and-passwords-from-your-site-here-s-how-9a8 ...

  3. ueditor和thinkphp框架整合修改版

    基于tp官网上的一篇文章修改的  因为tp中所有目录其实都是性对于入口文件的 在原来的基础上略做修改后 已经做到 无论项目放在www下的任何位置 图片在编辑器中回填后都能正常显示! http://fi ...

  4. Mysql中的primary key 与auto_increment

    mysql> create table cc(id int auto_increment); ERROR (): Incorrect table definition; there can be ...

  5. 【swupdate文档 四】SWUpdate:使用默认解析器的语法和标记

    SWUpdate:使用默认解析器的语法和标记 介绍 SWUpdate使用库"libconfig"作为镜像描述的默认解析器. 但是,可以扩展SWUpdate并添加一个自己的解析器, ...

  6. Linux内核中的队列 kfifo【转】

    转自:http://airekans.github.io/c/2015/10/12/linux-kernel-data-structure-kfifo#api 在内核中经常会有需要用到队列来传递数据的 ...

  7. NEERC2014

    NEERC2014 A - Alter Board 题目描述:给出一个\(n \times m\)的国际象棋棋盘,每次选定一个矩形,使得矩形中的每个格子的颜色翻转,求出最少次数的方案使得最终棋盘只有一 ...

  8. Python 正则表达式、re模块

    一.正则表达式 对字符串的操作的需求几乎无处不在,比如网站注册时输入的手机号.邮箱判断是否合法.虽然可以使用python中的字符串内置函数,但是操作起来非常麻烦,代码冗余不利于重复使用. 正则表达式是 ...

  9. [转载]锁无关的(Lock-Free)数据结构

    锁无关的(Lock-Free)数据结构 在避免死锁的同时确保线程继续 Andrei Alexandrescu 刘未鹏 译 Andrei Alexandrescu是华盛顿大学计算机科学系的在读研究生,也 ...

  10. 9.Python3标准库--数据压缩与归档

    ''' 尽管现代计算机系统的存储能力日益增长,但生成数据的增长是永无休止的. 无损(lossless)压缩算法以压缩或解压缩数据花费的时间来换取存储数据所需要的空间,以弥补存储能力的不足. Pytho ...