K
简单dp题,签到。
看数据范围大概猜到三维dp,然后记b的前几位,a的最长公共子序列,还有一维记怎么样才合法,记录左括号和右括号的差值。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
int mod=1000000007;
char s[2005];
ll dp[201][201][201];
void solve() {
memset(dp,0,sizeof(dp));
int n,m;
cin>>n>>m;
string s;
cin>>s;
dp[0][0][0]=1;
for(int i=1;i<=m;i++) {
for(int j=0;j<=i;j++) {
dp[i][0][j] += (j-1<0 ? 0 : dp[i-1][0][j-1]) + dp[i-1][0][j+1];
dp[i][0][j]%=mod;
}
}
for(int i=1;i<=m;i++) {
for(int j=1;j<=n;j++) {
for(int k=0;k<=i;k++){
int z = -1+2*(s[j-1]=='(');
if(k-z>=0) dp[i][j][k] += dp[i-1][j-1][k-z];
if(k+z<=m) dp[i][j][k] += dp[i-1][j][k+z];
dp[i][j][k] %= mod;
}
}
}
cout<<dp[m][n][0]<<endl;
}
int main() {
int t;
cin>>t;
while(t--) {
solve();
}
}
J
最小二乘题,可以套用公式,也可以用三元法,套公式的话要考虑到精度的问题,需要换一种更好的公式。
#include <cstdio>
#include <cctype>
#define MN 100000
using ll = __int128;
using ld = long double;
namespace GTI
{
char gc(void)
{
const int S = 1 << 16;
static char buf[S], *s = buf, *t = buf;
if (s == t) t = buf + fread(s = buf, 1, S, stdin);
if (s == t) return EOF;
return *s++;
}
ll gti(void)
{
ll a = 0, b = 1, c = gc();
for (; !isdigit(c); c = gc()) b ^= (c == '-');
for (; isdigit(c); c = gc()) a = a * 10 + c - '0';
return b ? a : -a;
}
int gts(char *s)
{
int len = 0, c = gc();
for (; isspace(c); c = gc());
for (; c != EOF && !isspace(c); c = gc()) s[len++] = c;
s[len] = 0;
return len;
}
int gtl(char *s)
{
int len = 0, c = gc();
for (; isspace(c); c = gc());
for (; c != EOF && c != '\n'; c = gc()) s[len++] = c;
s[len] = 0;
return len;
}
}
using GTI::gti;
using GTI::gts;
using GTI::gtl;
int a[MN+5];
void solve(){
int n;
ll s_xy=0,s_x=0,s_y=0,s_x2=0;
n = gti();
for(int i=1;i<=n;i++){
a[i] = gti();
s_xy += (ll)i*a[i];
s_x += i;
s_y += a[i];
s_x2 += (ll)i*i;
}
ld k = (ld)(n*s_xy-s_x*s_y)/(n*s_x2-(s_x)*(s_x));
ld b = ((ld)s_y/n)-k*((ld)s_x/n);
ld ans = 0;
for(int i=1;i<=n;i++){
ld y = k*i+b;
ans += (y-a[i])*(y-a[i]);
}
printf("%.15Lf\n",ans);
}
int main(){
int T;
T = gti();
while(T--) solve();
}
G
写一下赛时做法,生成一到n的全排列,大小小于10,用next_permutation,然后套一个最长上升,下降子序列,然后记录长度输出最长的,打表找规律,找到规律直接ac。
#include <bits/stdc++.h>
using namespace std;
int t,a[1000006];
void solve () {
int n;
cin>>n;
int num=0;
for(int i=1;i*i<=n;i++) num=i;
if(num*num<n) num+=1;
int z=1;
for(int i=n-num;i>=0;i-=num) {
for(int j=1;j<=num;j++) {
a[i+j]=z;
z++;
}
}
if(n%num!=0) {
int q=n%num;
int p=n-(n%num);
for(int i=n;i>=p+1;i--) {
a[q]=i;
q--;
}
}
for(int i=1;i<=n;i++) cout<<a[i]<<' ';
cout<<endl;
}
int main(){
int t;
cin>>t;
while(t--) {
solve();
}
return 0;
}
D
理解的话,就像我的世界刷物品一样?应该是。
然后很容易想到建图,有一个环就代表可以循环造一种物品。
考虑做一个d物品需要a/c个b物品,设置边权值为a/c,当环上的权值乘w然后累加,结果必须大于1。
w的值考虑二分答案,这是一个最大值最小的问题。
std代码封装得比较好,这里直接贴上。之后再放上自己写的吧,咕咕咕了。
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#define MN 1000
using std::log;
using std::queue;
using std::vector;
using ld = long double;
struct Edge{
int v;
ld w;
};
int n,m;
int din[MN+5];
vector<Edge> e[MN+5];
ld psw;
void addEdge(int u,int v,ld w){
e[u].push_back({v,w});
din[v]++;
}
struct Dis{
ld dis;
int cnt;
void reset(){
dis = 0;
cnt = 0;
}
void setInf(){
dis = 1e100;
cnt = 0;
}
bool operator < (const Dis& that)const{
return dis+cnt*psw < that.dis+that.cnt*psw;
}
Dis operator + (ld w)const{
return {dis+w,cnt+1};
}
};
Dis dis[MN+5];
bool inq[MN+5];
bool hasNegativeLoop(){
queue<int> q;
for(int i=1;i<=n;i++){
dis[i].reset();
q.push(i);
inq[i] = true;
}
while(!q.empty()){
int u = q.front();
q.pop();
inq[u] = false;
if(dis[u].cnt>=n) return true;
for(Edge edge:e[u]){
int v = edge.v;
ld w = edge.w;
if(dis[u]+w<dis[v]){
dis[v] = dis[u]+w;
if(!inq[v]){
q.push(v);
inq[v] = true;
}
}
}
}
return false;
}
bool check(ld psw){
::psw = psw;
return !hasNegativeLoop();
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,u,c,v;
scanf("%d%d%d%d",&a,&u,&c,&v);
ld w = -log((ld)c/a);
addEdge(u,v,w);
}
ld l=0,r=1;
for(int t=0;t<60;t++){
ld mid = (l+r)/2;
if(check(-log(mid))){
l = mid;
}else{
r = mid;
}
}
printf("%.10f\n",(double)r);
}