洛谷 - P2281 - 多项式的加法和乘法 - 大模拟
题目链接:https://www.luogu.org/problemnew/show/P2281
题目的意思很简单,输入两个系数、指数都是整数,变量都是大写字母的多项式,求他们的加法结果和乘法结果。
按照题目的意思模拟,先设计我们需要的类。
单项式
一个单项式由系数以及各个变量的指数组成,为了简单起见他们都是带符号数。
多项式
一个多项式由一个单项式的向量组成。
然后实现一些细节就可以了:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Mo {
//单项式由两个部分组成:带符号的系数+字母和带符号的指数
ll xs; //单项式的系数
ll zs[256]; //单项式的指数
Mo() {
xs=0;
memset(zs,0,sizeof(zs));
}
//单项式的小于序,按题目要求,从A到Z,从小到大排序
bool operator<(Mo &m) {
for(int i='A'; i<='Z'; i++) {
if(zs[i]!=m.zs[i]) {
return zs[i]<m.zs[i];
} else {
continue;
}
}
//为确定严格的小于序,规定指数相同的按系数排列
return xs<m.xs;
}
//指示两个单项式能否合并同类项
bool canMerge(Mo &m) {
//能够合并,前提是指数都相同
for(int i='A'; i<='Z'; i++) {
if(zs[i]!=m.zs[i]) {
return false;
} else {
continue;
}
}
return true;
}
//单项式的乘法,系数相乘,对应指数相加
Mo operator*(Mo &m) {
Mo ret;
ret.xs=xs*m.xs;
for(int i='A'; i<='Z'; i++) {
ret.zs[i]=zs[i]+m.zs[i];
}
return ret;
}
//单项式的带符号输出,即使在多项式的开头也输出符号
string toString() {
if(xs==0) {
//多项式不应该有系数为0的单项式,都进入这里了直接RE吧,为0的项在读入和加法的时候应被消除
exit(-1);
return "";
}
string ans="";
char tmp[25]; //用来把数字转成字符串
if(xs>0) {
//系数为正,加入单项式前的加号
ans+="+";
if(xs>1) {
//非1的系数必须打印
sprintf(tmp,"%lld",xs);
ans+=string(tmp);
}
} else {
//负数自带符号,但-1不输出那个1,就单输出一个负号
if(xs==-1) {
ans+="-";
} else {
//非-1的系数必须打印
sprintf(tmp,"%lld",xs);
ans+=string(tmp);
}
}
int nozs=1; //指示这个多项式是否 没有指数非零
for(int i='A'; i<='Z'; i++) {
if(zs[i]!=0) {
nozs=0; //有指数非零
//指数非0,输出该字母
ans+=(char)(i);
if(zs[i]!=1) {
//指数非1,额外输出指数,负数也一样可以输出
ans+="^";
sprintf(tmp,"%lld",zs[i]);
ans+=string(tmp);
}
}
}
if(nozs) {
//没有指数非零,这个是常数项
if(abs(xs)==1)
//常数项的正负1要输出
ans+="1";
}
return ans;
}
};
struct Poly {
//存放单项式的向量
vector<Mo> v;
Poly() {}
//加法,两个多项式的单项式堆在一起,合并同类项
Poly operator+(Poly p) {
Poly ret;
for(auto vi:v) {
ret.v.push_back(vi);
}
for(auto vi:p.v) {
ret.v.push_back(vi);
}
ret.Merge();
return ret;
}
//乘法,二维遍历每个单项式,乘在一起,然后堆在一起合并同类项
Poly operator*(Poly p) {
Poly ret;
for(auto vi:v) {
for(auto vpi:p.v) {
ret.v.push_back(vi*vpi);
}
}
ret.Merge();
return ret;
}
//合并同类项并排序
void Merge() {
//临时向量vt
vector<Mo> vt;
for(auto &vi:v) {
//遍历现有的每个项,在临时向量中找它已经插入的同类项,若找到可以合并的项,系数相加
int suc=0;
for(auto &vti:vt) {
if(vti.canMerge(vi)) {
vti.xs+=vi.xs;
suc=1;
break;
}
}
if(suc==0) {
//没有同类项,则接在临时向量的后面
vt.push_back(vi);
}
}
v.clear();
//清空原向量
for(auto vti:vt) {
//遍历临时向量,去除系数为0的项
if(vti.xs) {
v.push_back(vti);
}
}
//排序,按单项式定义的顺序排序
sort(v.begin(),v.end());
}
//返回一整个排序好的多项式,并自动去除最前面的正号
string toString() {
//多项式中没有单项式,返回一个0
if(v.size()==0)
return "0";
Merge(); //合并同类项并排序
string ans="";
for(auto vi:v)
ans+=vi.toString();
//前面已经保证多项式至少有一个单项式,而且它至少会输出一个负号,故可以ans[0]
//去除开头多余的+号
if(ans[0]=='+') {
ans=ans.substr(1,ans.length()-1);
}
return ans;
}
void fromString(string s) {
int n=s.length();
ll xs=0; //带符号系数
ll zs[256]; //带符号指数
memset(zs,0,sizeof(zs));
for(int i=0; i<=n; i++) {
if(i==n) {
//到达字符串结尾,保存最后一个单项式
addMo(xs,zs);
return;
}
if(s[i]=='+'||s[i]=='-'||isdigit(s[i])) {
//遇到数字,处理到直到遇到下一个字母或者运算符或者结尾
//算法的逻辑保证遇到的必定是系数而不是指数
//保存最后一个单项式
addMo(xs,zs);
int flag=1;
if(!isdigit(s[i])) {
//当前遇到的是符号
//cerr<<"+"<<endl;
if(s[i]=='-')
flag=-1;
i++;
//指向符号的下一个字符
if(i==n) {
//到达字符串结尾,保存最后一个单项式?
//符号后面都没有东西,系数是0,不用添加单项式
return;
}
if(isdigit(s[i])) {
//下一个是显式指定的数字,处理到第一个非数字
while(isdigit(s[i])) {
xs=10ll*xs+(s[i]-'0');
i++;
if(i==n) {
//到达字符串结尾,保存最后一个单项式,也就是常数项,当然要注意符号
xs*=flag;
addMo(xs,zs);
return;
}
}
//现在s[i]是非数字,下次for会i++,这里补偿--
i--;
//保存符号
xs*=flag;
} else {
//符号后面不是数字,系数是隐含的1
xs=1;
//现在s[i]是非数字,下次for会i++,这里补偿--
i--;
//保存符号
xs*=flag;
}
} else {
//没有遇到符号就直接遇到数字,是多项式的开头
while(isdigit(s[i])) {
xs=10ll*xs+(s[i]-'0');
i++;
if(i==n) {
//到达字符串结尾,保存最后一个单项式,也就是常数项,当然要注意符号,虽然一定是+1
xs*=flag;
addMo(xs,zs);
return;
}
}
//现在s[i]是非数字,下次for会i++,这里补偿--
i--;
//保存符号,虽然一定是+1
xs*=flag;
}
} else if(isalpha(s[i])) {
//遇到字母,处理到直到遇到下一个字母或者运算符或者结尾
//没有遇到运算符和数字就遇到多项式开头的字母,系数为1
if(xs==0)
xs=1;
//保存这个字母,名字叫做c
int c=s[i];
//cout<<(char)(c)<<"!!!"<<endl;
//指向下一个字符
i++;
if(i==n) {
//到达字符串结尾,保存最后一个单项式
//先把最后一个字符的1次方加上
zs[c]++;
addMo(xs,zs);
return;
}
if(s[i]=='^') {
//是指数符号,说明要继续处理
i++;
//指向下一个数字或者符号
int flag=1;
if(!isdigit(s[i])) {
//当前遇到的是符号
if(s[i]=='-')
flag=-1;
i++;
//指向符号的下一个字符
if(i==n) {
//到达字符串结尾,你符号后面居然没有数?那就认为指数是0吧,舍弃最后一个字母,保存单项式
addMo(xs,zs);
return;
}
ll tzs=0;
if(isdigit(s[i])) {
//下一个是显式指定的数字,处理到第一个非数字
while(isdigit(s[i])) {
tzs=10ll*tzs+(s[i]-'0');
i++;
if(i==n) {
//到达字符串结尾,保存最后一个单项式
//保存最后一个字母的指数,记得乘上符号
zs[c]+=tzs*flag;
addMo(xs,zs);
return;
}
}
//现在s[i]是非数字,下次for会i++,这里补偿--
i--;
//保存符号
tzs*=flag;
//保存指数
zs[c]+=tzs;
} else {
//符号后面不是数字,系数是隐含的1
tzs=1;
//现在s[i]是非数字,下次for会i++,这里补偿--
i--;
//保存符号
tzs*=flag;
//保存指数
zs[c]+=tzs;
}
} else {
//没有遇到符号就直接遇到数字,是正的指数
//cerr<<"...\n"<<endl;
ll tzs=0;
while(isdigit(s[i])) {
tzs=10ll*tzs+(s[i]-'0');
i++;
if(i==n) {
//到达字符串结尾,保存最后一个单项式,也就是常数项,当然要注意符号,虽然一定是+1
tzs*=flag;
zs[c]+=tzs;
addMo(xs,zs);
return;
}
}
//现在s[i]是非数字,下次for会i++,这里补偿--
i--;
//保存符号,虽然一定是+1
tzs*=flag;
//保存指数
zs[c]+=tzs;
}
} else {
//遇到了字母或运算符,保存指数并补偿--
zs[c]++;
i--;
}
}
}
}
//向多项式中添加一个单项式
void addMo(ll &xs,ll *zs) {
if(xs==0)
return;
Mo m;
m.xs=xs;
memcpy(m.zs,zs,sizeof(m.zs));
v.push_back(m);
xs=0;
//不能sizeof(zs),因为这里zs不是数组而只是一个指针
memset(zs,0,sizeof(ll)*256);
}
};
char s[10005],t[10005],n;
int main() {
#ifdef Yinku
freopen("Yinku.in","r",stdin);
#endif // Yinku
fgets(s,10000,stdin);
n=strlen(s);
for(int i=0,j=0; i<=n; i++) {
if(i==n) {
t[j]='\0';
}
if(s[i]!=' '&&s[i]!='\n') {
t[j++]=s[i];
}
}
Poly A;
A.fromString(string(t));
fgets(s,10000,stdin);
n=strlen(s);
for(int i=0,j=0; i<=n; i++) {
if(i==n) {
t[j]='\0';
}
if(s[i]!=' '&&s[i]!='\n') {
t[j++]=s[i];
}
}
Poly B;
B.fromString(string(t));
#ifdef Yinku
cout<<A.toString()<<endl;
cout<<B.toString()<<endl;
#endif // Yinku
Poly C=A+B;
Poly D=A*B;
cout<<C.toString()<<endl;
cout<<D.toString()<<endl;
}
洛谷 - P2281 - 多项式的加法和乘法 - 大模拟的更多相关文章
- 洛谷P1067 多项式输出 NOIP 2009 普及组 第一题
洛谷P1067 多项式输出 NOIP 2009 普及组 第一题 题目描述 一元n次多项式可用如下的表达式表示: 输入输出格式 输入格式 输入共有 2 行 第一行 1 个整数,n,表示一元多项式的次数. ...
- 洛谷1373 小a和uim之大逃离
洛谷1373 小a和uim之大逃离 本题地址:http://www.luogu.org/problem/show?pid=1373 题目背景 小a和uim来到雨林中探险.突然一阵北风吹来,一片乌云从北 ...
- 洛谷 P1373 小a和uim之大逃离
2016-05-30 12:31:59 题目链接: P1373 小a和uim之大逃离 题目大意: 一个N*M的带权矩阵,以任意起点开始向右或者向下走,使得奇数步所得权值和与偶数步所得权值和关于K的余数 ...
- 【题解】洛谷P1373 小a和uim之大逃离(坐标DP)
次元传送门:洛谷P1373 思路 设f[i][j][t][1/0]表示走到(i,j)时 小a减去uim的差值为t 当前是小a取(0) uim取(1) 那么转移就很明显了 f[i][j][t][]=(f ...
- [uoj#34] [洛谷P3803] 多项式乘法(FFT)
新技能--FFT. 可在 \(O(nlogn)\) 时间内完成多项式在系数表达与点值表达之间的转换. 其中最关键的一点便为单位复数根,有神奇的折半性质. 多项式乘法(即为卷积)的常见形式: \[ C_ ...
- 洛谷 P3803 多项式乘法
题目背景 这是一道FFT模板题 题目描述 给定一个n次多项式F(x),和一个m次多项式G(x). 请求出F(x)和G(x)的卷积. 输入输出格式 输入格式: 第一行2个正整数n,m. 接下来一行n+1 ...
- 洛谷 P3803 多项式乘法(FFT) —— FFT
题目:https://www.luogu.org/problemnew/show/P3803 终于学了FFT了! 参考博客:https://www.cnblogs.com/zwfymqz/p/8244 ...
- 模板【洛谷P3811】 【模板】乘法逆元
P3811 [模板]乘法逆元 给定n,p求1~n中所有整数在模p意义下的乘法逆元. T两个点的费马小定理求法: code: #include <iostream> #include < ...
- 洛谷——P1067 多项式输出
P1067 多项式输出 题目描述 一元 n 次多项式可用如下的表达式表示: 其中,aixi称为 i 次项,ai 称为 i 次项的系数.给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该 ...
随机推荐
- 无线(仅WIFI)攻击思路总结
从事信息安全相关工作5年了,虽然主要工作是安全产品售前.安全服务等方向,但既然选择了安全,想必都是有点黑客情节的,因此也前前后后杂七杂八的学了点东西.最近在研究无线(主要是WIFI)安全,相关书籍看了 ...
- 【LeetCode with Python】 Sort List
博客域名:http://www.xnerv.wang 原题页面:https://oj.leetcode.com/problems/sort-list/ 题目类型: 难度评价:★ 本文地址:http:/ ...
- maven的坑2
导入工程后,pom.xml文件中以下插件报错: <plugin> <groupId>com.jayway.maven.plugins.android.generation2&l ...
- 关于mongorc.js文件详解
最近阅读了<<mongodb权威指南第二版>>,发现这本书比之前的第一版好,很多地方讲解很详细.下面就翻译下谈下这个文件. 首先,启动shell的时候,mongorc.js文件 ...
- MAC如何查看某个端口的占用情况
执行如下命令: lsof -i tcp:8080 #8080为查询的端口号 会展示该端口的使用情况,然后kill -9 PID的值即可关闭该端口
- 九度OJ 1088:剩下的树 (线段树)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5791 解决:2649 题目描述: 有一个长度为整数L(1<=L<=10000)的马路,可以想象成数轴上长度为L的一个线段,起点 ...
- 支付宝cookie 是支付密码 不是登录密码
开发文档/ 手机网站支付 / 产品介绍 开放平台文档中心 https://docs.open.alipay.com/203/105288
- WIN7系统设置wifi
*&->20170302 112700 WIN7系统设置wifi, 开启win7的隐藏功能,即虚拟wifi功能和虚拟无线AP功能,即可实现将电脑变成wifi 供无线上网, 1.开始-命令 ...
- angularjs ng-repeat倒叙
<div ng-app="myApp" ng-controller="customersCtrl"> <table> <tr ng ...
- android SDK中添加自定义api【转】
本文转载自:http://blog.csdn.net/a624731186/article/details/23548409 本文的思路: android APP调用自定义java API,自定义ja ...