题目链接:https://cometoj.com/contest/34/problems
A.解方程
,(由公式可知y>=z,0<=n<=根号x)移项再平方可得
那么如果n是个平方数,很明显解有无穷多个。
否则必须满足 n = 4*y*z才有整数解,即 x = (z+y)。所以只要枚举n/4的所有因子就好了。(注意这样做卡longlong,要想不卡去筛素数吧)
#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pa;
typedef long long ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
int main() {
int n;
int t;scanf("%d",&t);
while(t--){
scanf("%d",&n);
ll k = sqrt(n);
if(k*k==n) puts("infty");
else{
if(n%4!=0) puts("0 0");
else{
ll ans = 0;
int ca = 0;
n /= 4;
for(int i=1;i*i<=n;i++){
if(n%i==0){
ans += 1ll*i*(n/i)%mod*(i+n/i)%mod;
ans %= mod;
ca++;
}
}
printf("%d %lld\n",ca,ans);
}
}
}
return 0;
}
B.旅途
由题意可知最终操作了m-1次,所以乘上100^(m-1)就保证了数为整数,直接抵消分母。
由题意可知m天恰好游玩i个城市肯定是连续的一段,那么我可以想去dp。
最普通的dp就是dp[q][i][j][k]表示旅游了q天,除了(i,j)区间内城市其他城市都旅游过了,且现在在k城市,转移方程就是在k处两种选择顺时针,逆时针,停留。如果k在i或者j就说明可能旅游新的城市了。
但是O(n^4)肯定是过不了的(没想到O(n^3)跑那么快,真得狗)。
所以我们就想办法怎么用<4维的dp去维护四维的变量。首先天数q肯定是一定有的,所以剩下只能缩小后面三个,但是我们仔细想一想k在哪重要吗?重要的是k是否在i或者j上,换句话说判断k和i或者j的距离是否为0就好了,还有一个重要的变量就是得知道旅游了多少个城市,城市个数可以化为k和i的距离+k和j的距离+1。所以最后四维dp就可以优化为三维dp:
dp[i][j][k]表示旅游了i天,现在在的城市距离已经旅游过的城市的左端点j距离,和距离已经旅游过的城市的右端点k距离。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 505;
const int mod = 1e9 + 7;
int dp[mx][mx][mx];
ll q,p,r,fac[mx];
int main(){
//freopen("1.in","r",stdin);
int t,n,m,K;scanf("%d",&t);
fac[0] = 1;for(int i=1;i<mx;i++) fac[i] = fac[i-1]*100%mod;
while(t--){
scanf("%d%d%d%lld%lld",&n,&m,&K,&p,&q);
r = 100 - p - q;
dp[1][0][0] = 1;
for(int i=2;i<=m;i++)
for(int j=0;j<min(i,n);j++)
for(int k=0;k+j<min(i,n);k++) dp[i][j][k] = 0;
for(int i=1;i<m;i++){
int mi = min(i,n);
for(int j=0;j<mi;j++){
for(int k=0;k+j<mi;k++){
if(dp[i][j][k]){
if(j+k+1==n){
dp[m][j][k] = (dp[m][j][k] + dp[i][j][k]*fac[m-i]%mod)%mod;
continue;
}
dp[i+1][max(0,j-1)][k+1] = (dp[i+1][max(0,j-1)][k+1]+dp[i][j][k]*q%mod)%mod;
dp[i+1][j+1][max(0,k-1)] = (dp[i+1][j+1][max(0,k-1)]+dp[i][j][k]*p%mod)%mod;
dp[i+1][j][k] = (dp[i+1][j][k]+dp[i][j][k]*r%mod)%mod;
}
}
}
}
ll ans = 0;
for(int i=1;i<=n;i++){
ll mul = 1,ret = 0;
for(int j=1;j<=K;j++) mul = mul*i%mod;
for(int j=0;j<i;j++){
ret = (ret + dp[m][j][i-j-1])%mod;
}
ans = (ans+mul*ret%mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
C.项链与计数
还没补
D.战术安排
还是想着中规中矩的DP(数据这么小一直想着怎么去暴力,但是都不成),正因为数据小,所以我们也可以想怎么DP。
dp[i][j][k]表示i二进制位表示6个题目的做与不做的情况下,猫在第j分钟后无事可做和兔在第k分钟后无事可做的情况下象最少在第几分钟后无事可做。
原来一直想着不用状态压缩,因为不用去考虑他们的做题顺序,但是大错特错,他们的做题顺序很明显是有关系的。比如我AB组合做完B就可以再做一题了,但是要是先让AC做,那么AB就被卡住了,使得B无事可做的时间延后了。所以顺序是有关系的。
所以最后的时间复杂度是O(50*2^(6)*180*180*6),虽然复杂度有点高,但是跑起来还行。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int mx = 1e5 + 10;
int dp[1<<7][200][200];
int a[10][10];
void uptd(int i,int j,int k,int v)
{
if(max(j,max(k,v))<=180)
dp[i][j][k] = min(dp[i][j][k],v);
}
int main(){
//freopen("1.in","r",stdin);
int t,c,d;scanf("%d",&t);
while(t--){
int len = (1<<6);
for(int i=0;i<6;i++){
for(int j=1;j<=7;j++) scanf("%d",a[i]+j);
}
memset(dp,inf,sizeof(dp));
dp[0][0][0] = 0;
for(int i=0;i<len;i++){
for(int j=0;j<=180;j++){
for(int k=0;k<=180;k++){
int v = dp[i][j][k];
if(dp[i][j][k]!=inf)
for(int w=0;w<6;w++){
if((1<<w)&i) continue;
int s = (1<<w)|i;
uptd(s,j+a[w][1],k,v);
uptd(s,j,k+a[w][2],v);
uptd(s,j,k,v+a[w][3]);
c = max(j,k) + a[w][4];
uptd(s,c,c,v);
c = max(j,v) + a[w][5];
uptd(s,c,k,c);
c = max(k,v) + a[w][6];
uptd(s,j,c,c);
c = max(j,max(k,v)) + a[w][7];
uptd(s,c,c,c);
}
}
}
}
int ansp = 0,ansv = 0;
for(int i=0;i<len;i++){
for(int j=0;j<=180;j++){
for(int k=0;k<=180;k++){
if(dp[i][j][k]!=inf){
c = max(j,max(k,dp[i][j][k]));
d = __builtin_popcount(i);
if(d>ansp||(d==ansp&&ansv>c))
ansp = d,ansv = c;
}
}
}
}
printf("%d %d\n",ansp,ansv);
}
return 0;
}
E.最长上升子序列
用pre[i]表示前缀以a[i]结尾的最长上升子序列长度。
用suf[i]表示后缀以a[i]结尾的最长下降子序列长度。
情况1:改变ax可以使得原来最长上升子序列的长度cnt+1。
如果有pre[i]+suf[j] == cnt 且 i<j 且a[i]+1<a[j]。那么就可以再(i,j)区间内任意一个地方变为a[i]+1,就可以cnt+1。
换句话说固定左区间i,寻找最远的j使得可以再(i,j)中插入a[i]+1使得cnt+1。
这个可以用很多方法解决,最直接的方法就是用set。
情况2:不能使得cnt+1,但可以把ax变为0
1.没有ax原序列的最长上升子序列还是cnt。(找到pre[x]==pre[y] 且 pre[y]+suf[y]==cnt+1)
2.ax在原上升子序列的第一个(pre[x]==1),那么显然可以把它变为0。
3.存在x<y 且 suf[y] == cnt - 1。此时可以把ax变为0作为第一个。
情况3:不能使cnt+1,只能不变或更小但不能变为0
实际上这个情况和情况1是一样的,当情况1,2都不存在时,如果有:
如果有pre[i]+suf[j] == cnt-1 且 i<j 且a[i]+1<a[j]。那么就可以再(i,j)区间内任意一个地方变为a[i]+1,如果本来的数大于a[i]+1,那么就可以变小了。且最后的长度还是cnt。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pa;
const int mx = 1e5 + 10;
int a[mx],b[mx],q[mx];
int n,pre[mx],suf[mx];
int pos[2][mx],can[mx];
set <pa> du[2][mx];
multiset <int> s[2];
vector <int> g[2][mx];
bool ok[mx];
void getup(int v,int id,int len){
for(int i=n;i>=1;i--){
int w = len - suf[i] - v;
if(w<0) continue;
auto it = du[id][w].begin();
while(it!=du[id][w].end()){
if(it->fi+1>=a[i]) break;
if(it->se<i){
pos[id][it->se] = i;
g[id][i].push_back(it->fi);
}
du[id][w].erase(it++);
}
}
}
void init(){
for(int i=0;i<=n;i++){
pos[0][i] = pos[1][i] = 0;
g[0][i].clear();g[1][i].clear();
du[0][i].clear();du[1][i].clear();
s[0].clear();s[1].clear();
can[i] = ok[i] = 0;
}
}
void solve(int len){
for(int i=1;i<=n;i++){
du[1][i] = du[0][i];
if(suf[i]==len&&a[i]){
s[0].insert(-1);
g[0][i].push_back(-1);
}
if(suf[i]==len-1&&a[i]){
s[1].insert(-1);
g[1][i].push_back(-1);
}
if(pre[i]+suf[i]==len+1)
can[pre[i]]++,ok[i] = 1;
}
getup(0,0,len);getup(1,1,len);
for(int i=1;i<=n;i++){
for(int j=0;j<2;j++){
for(int k:g[j][i])
s[j].erase(s[j].lower_bound(k));
}
if(s[0].size()){
printf("%d %d\n",len+1,*s[0].begin()+1);
}else{
if(pre[i]==1||can[pre[i]]>1||!ok[i])
printf("%d 0\n",len);
else printf("%d %d\n",len,*s[1].begin()+1);
}
if(pos[0][i]||pre[i]==len) s[0].insert(a[i]);
if(pos[1][i]||pre[i]==len-1) s[1].insert(a[i]);
}
}
int main(){
//freopen("1.in","r",stdin);
int T; scanf("%d",&T);
while (T--){
init();
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i),b[i] = -a[i];
int k = 1;
q[0] = a[1],pre[1] = 1;
du[0][1].insert(pa(a[1],1));
for(int i=2;i<=n;i++){
int w = lower_bound(q,q+k,a[i]) - q;
pre[i] = w+1,q[w] = a[i];
if(w==k) k++;
du[0][pre[i]].insert(pa(a[i],i));
}
k = 1,q[0] = b[n],suf[n] = 1;
for(int i=n-1;i>=1;i--){
int w = lower_bound(q,q+k,b[i]) - q;
suf[i] = w+1,q[w] = b[i];
if(w==k) k++;
}
solve(k);
}
return 0;
}
F.木棍与多边形
还没补。