梯度求解
很有启发性的一道题!这道题让我收获了如何用map表示公式:公式,说白了就是多项式。而多项式又是由众多单项式组成的,所以,我们需要先学会表达单项式。单项式形如:cX^e,需要一个值来表示系数,一个值来表示指数,对于只有一个变量的单项式,我们可以有很多种方式来表达。但是,考虑到单项式多了,有些单项式没有整合到一起,比如 3X^2+6X^2 ,相同指数的项是可以合并系数的。为了方便我们直接定位到指数的系数,我们使用 map<int,int> ,分别存储c和e,来表示一个单项式。
x1*x1*x1+x1*3转换为
指数1 系数3
指数3 系数1
在该map中添加多个c,e组合,也就意味着我们添加了多个单项式,这个map就是成为了一个多项式。
为了转化成上面的形式,我们需要提前将那些不重要的变量(也就是将那些不求导的变量)转化为系数(对于不求导的变量而言,这一步可以提前处理,也可以最后处理,都一样,为了方便,我们提前处理),然后再运算。
AC:
#include <iostream>
#include <sstream> //用于处理获取空格隔开的字符串
#include <stack>
#include <string>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
#define ll long long
const int MOD=1e9+7;
const int N=1e2+2;
/*
公式怎么表示?
首先,将公式化简。无关的变量全部转化为系数。如x1+x1*x1*x2,求x1的倒数x2=2,那么就为x1+2*x1^2这种
然后我们需要表示的是单独存在x1的项的系数和指数即可。推荐使用map<ll,ll>,分别表示 指数、系数
map外面套一层stack,便于我们对整体公式进行整合
*/
int n,m; //自变量个数,要求解的偏导数的个数
vector<string>vec;
stack<map<ll,ll>>func;
int main()
{
cin>>n>>m;
stringstream linestream;
string line,t;
getchar(); //getline之前都来杯getchar
getline(cin,line);
linestream.str(line); //相当于这里将其空格处理
while(linestream>>t) vec.push_back(t);
int id;
vector<int>x(n+1,0); //n个元素
while(m--)
{
cin>>id;
for(int i=1;i<=n;i++) cin>>x[i];
string goal="x"+to_string(id); //首先记录下了求导的变量
for(int i=0;i<vec.size();i++) //遍历容器
{
t=vec[i]; //t可能是 运算符、xi(xi分为goal和非goal)、常数
//在前期,一般只会出现单个的项,如x1,x2这种,此时就是将其转换成系数的最好时机
if(t==goal) //如果当前得到的项是要求导的项
{
map<ll,ll>mp;
mp[1]=1;
func.push(mp); //直接保留
}
else if(t[0]=='x') //没有进入goal的且为x,说明是非goal变量
{
//将其转换为系数
// int tempId=stoi(t[1]); //字符串转整型:stoi(string str);stol、stoll、stod(double)
// 上面因为t[1]为char,倒不能直接转,而是先要转为string,用substr
int tempId=stoi(t.substr(1)); //substr(size_t pos,size_t len)
map<ll,ll>mp;
mp[0]=x[tempId];
func.push(mp);
}
//上面两种情况只在前期容易出现,也就是单项式的情况。下面将这些单项式逐渐转换为多项式,并进行运算
else if(t=="+"||t=="-"||t=="*") //如果是运算符
{
//会将栈中两个元素弹出,进行运算操作
//这里我们用两个map接收两个元素(其实本质是多项式)
map<ll,ll>mp2=func.top(); func.pop();
map<ll,ll>mp1=func.top(); func.pop();
map<ll,ll>mp; //用来表示运算结果
if(t=="+")
{
//加法运算,如 3*x1+5*x1,相同指数的系数相加
mp=mp1;
for(auto &it:mp2) //first是指数,second是系数
mp[it.first]=(mp[it.first]+it.second)%MOD; //该式本质是 同指数下的mp.second+mp2.second
}
else if(t=="-") //减法同理
{
mp=mp1;
for(auto &it:mp2) //first是指数,second是系数
mp[it.first]=(mp[it.first]-it.second)%MOD;
}
else
{ //乘法就比较复杂:需要将mp1的所有项都和mp2的所有项相乘
//而单独两项相乘,就是系数相乘,指数相加
//就结果而言,结果的指数对应的系数就是两个系数乘后的累加
for(auto &it1:mp1)
for(auto &it2:mp2)
{
mp[it1.first+it2.first]+=it1.second*it2.second;
mp[it1.first+it2.first]%=MOD;
}
}
func.push(mp); //将运算结果多项式放回去
}
else //数字
{
ll data=stoll(t); //转换为longlong
map<ll,ll>mp;
mp[0]=data;
func.push(mp);
}
}
//最后func中得到的是一个关于goal的多项式
//接下来我们需要将这个多项式进行求导操作
//其实这里就简单了,指数-1,指数为0的舍弃,然后将值带入
ll ans=0;
map<ll,ll>f=func.top();
for(auto &it:f)
{
ll e=it.first; //指数 原指数会-1
ll c=it.second; //系数 原系数会*e
if(e==0) continue;
ans=(ans+c*e*(ll)pow(x[id],e-1))%MOD;
}
//下面是链接中大佬给的源代码,我不太理解,所以换成了上面的,也是满分
// ll ans=0,pree=0,fac=1;
// map<ll,ll>f=func.top();
// for(auto &it:f)
// {
// ll e=it.first; //指数
// ll c=it.second; //系数
// for(int i=pree+1;i<e;i++)
// fac=fac*x[id]%MOD;
// pree=e==0?0:e-1;
// ans=(ans+c*e*fac)%MOD;
// }
cout<<(ans+MOD)%MOD<<endl; //因为负数也要求在 [0,MOD)的范围内,所以需要+MOD使其变成正数
//如果本来就是正数也无所谓,+mod后会%去
}
return 0;
}