比较裸的$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. NYOJ 136 等式 (哈希)

    题目链接 描述 有以下等式:a1x13+a2x23+a3x33+a4x43+a5*x53=0 x1,x2,x3,x4,x5都就在区间[-50,50]之间的整数,且x1,x2,x3,x4,x5都不等于0 ...

  2. JS中短路运算符&&和||

    在JS函数中我们经常会使用到短路运算符,主要是逻辑与(&&) 和 逻辑或(||) 1.逻辑与 && 的运算方式 var a = 5 && 6; cons ...

  3. 巅峰极客CTF writeup[上]

    经验教训 1.CTF不比实战,最好不要死磕.死磕就输了.我就是死磕在缓存文件死的.真的惭愧: 2.对于flag的位置不要太局限于web目录下,如果是命令执行直接上find / -name flag*: ...

  4. Freemaker如何遍历key为non-string类型的map?

    (一) 前置知识 Freemaker默认配置下会使用SimpleHash去包装后台传递的hashmap,下段摘抄自官方reference 同样,当你传递进去一个hashmap实例时,会替换为一个sim ...

  5. 进度条算法 progressBar

    ; ;var maxNum=int.MaxValue; progressBar.Maximum =maxNum; progressBar.Minimum = ; progressBar.Value = ...

  6. TCP三次链接和四次断开

    经典的三次握手示意图:(#add,“握手”即图中左边到右边的连线)    经典的四次握手关闭图:    TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: 位码即t ...

  7. JavaScript 执行环境 与 变量对象

    什么是JS的执行环境? function funA(){ //一段代码静静的躺在这里,不能叫执行环境 } funA(); //当代码开始执行以后,系统会将它存入执行栈,并为他准备好足够的内存空间使用 ...

  8. 以Docker容器方式安装Ceph

    获取Ceph的Docker镜像 因为公司对于网络环境的限制,安装ceph时使用ceph-deploy反而很不方便,且ssh免密码方式也不适用,所以使用docker方式安装. Git地址 https:/ ...

  9. MIAC HW2

    MIAC的第二次作业,翻了一些fashion网站找了点灵感,重新设计了一下UI. 因为给的html里有nav之类的HTML5新特性,所以索性就不管IE的兼容了.chrome下的效果: FF下也差不多. ...

  10. office 文档转pdf

    本地先安装 金山wps,并确保可用 工程目录 1.使用前,先执行install.bat 安装jacob 到maven本地仓库 2.复制 jacob-1.18-M2-x64.dlljacob-1.18- ...