【学长出题】【比赛题解】17-10-29

题目很有趣,我觉得很有必要写一写。

【T1】买

题意:

\(n\)个人买\(m\)个物品,第\(i\)个人买的物品价格必须大于等于\(a[i]\),美观度必须大于等于\(b[i]\)。

第\(i\)个物品的价格和美观度分别是\(c[i]\),\(d[i]\)。

求出要满足所有人的最小花费。

题解:

考虑把两个限制转换成一个限制,即我们对物品按照美观度排序,那么一个人对应的可买就是一段连续的的区间了(不考虑价格),方便处理。

而对于价格最小,在所有的可买物品中,选取大于等于\(a[i]\)的物品,这可以使用平衡树(set)维护。

仔细品味一下,这就是要以美观度,而不是价格排序的原因。

如果把人也按照美观度排序,就可以按顺序添加物品,方便处理。

其实是个贪心,正确性与最优性不难证明。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<set>
 5 #define F(i,a,b) for(int i=a;i<=b;++i)
 6 #define F2(i,a,b) for(int i=a;i<b;++i)
 7 using namespace std;
 8 int n,m,a[100001],b[100001],c[100001],d[100001],I[100001],J[100001];
 9 long long ans=0;
10 inline int cmp1(int p1,int p2){return b[p1]>b[p2];}
11 inline int cmp2(int p1,int p2){return d[p1]>d[p2];}
12 multiset<int> s;
13 multiset<int>::iterator iter;
14 void init(){
15     scanf("%d%d",&n,&m);
16     F(i,1,n) scanf("%d%d",a+i,b+i),I[i]=i;
17     F(i,1,m) scanf("%d%d",c+i,d+i),J[i]=i;
18     std::sort(I+1,I+n+1,cmp1); std::sort(J+1,J+m+1,cmp2);
19 }
20 int main(){
21     freopen("buy.in","r",stdin);
22     freopen("buy.out","w",stdout);
23     init();
24     int j=1;
25     F(i,1,n){
26         while(d[J[j]]>=b[I[i]]) s.insert(c[J[j]]), ++j;
27         iter=s.lower_bound(a[I[i]]);
28         if(iter!=s.end()) ans+=*iter, s.erase(iter); 
29         else {puts("-1"); return 0;}
30     } printf("%lld",ans);
31     return 0;
32 }

【T2】加密

题意:

给一个数列\(c[1\cdots n]\),定义变换\(A^x(c_i)=\sum_{j\neq i}A^{x-1}(c_j)\,mod\,98765431\;(x\geq 1)\),而\(A^0(c_i)=c_i\)。

求出\(A^x(c_i)\)。

题解:

先推式子,用\(sum\)表示初始元素的总和,用\(now\)表示这个元素,有\(\sum now=sum\),虽然\(now\)各不相同。

有\(x=1\)时,\(res=sum-now\)。

\(x=2\)时,\(res=(n-2)sum+now\)。

\(x=3\)时,\(res=(n-1)(n-2)sum+sum-now\)。

\(x=4\)时,\(res=(n-1)^2(n-2)sum+(n-2)sum+now\)。

\(x=5\)时,\(res=(n-1)^3(n-2)sum+(n-1)(n-2)sum+sum-now\)。

\(x=6\)时,\(res=(n-1)^4(n-2)sum+(n-1)^2(n-2)sum+(n-2)sum+now\)。

\(x=7\)时,\(res=(n-1)^5(n-2)sum+(n-1)^3(n-2)sum+(n-1)(n-2)sum+sum-now\)。

\(\vdots\)

于是有:

当\(x=2k+1\)是奇数时,\(res=[\sum_{i=0}^{k-1}[(n-1)^2]^i](n-1)(n-2)sum+sum-now\)。

当\(x=2k\)是偶数时,\(res=[\sum_{i=0}^{k-1}[(n-1)^2]^i](n-2)sum+now\)。

其中\(\sum_{i=0}^{k-1}[(n-1)^2]^i\)部分相同,其实是可以化简的,化简为\(\frac{(n-1)^{2k}-1}{(n-1)^2-1}\)。

于是直接计算就做完了,注意求逆元。

 1 #include<cstdio>
 2 #define Mod 98765431
 3 long long n,t,a[50001],sum=0;
 4 long long M(long long x,long long y){return x*y%Mod;}
 5 int Pow(long long base,long long exp){
 6     long long ans=1;
 7     for(;exp;(exp&1)?ans=M(ans,base):(0), base=M(base,base), exp>>=1);
 8     return ans;
 9 }
10 int main(){
11     freopen("password.in","r",stdin);
12     freopen("password.out","w",stdout);
13     scanf("%I64d%I64d",&n,&t);
14     for(int i=1;i<=n;++i) scanf("%lld",a+i),sum=(sum+a[i])%Mod;
15     long long W=Pow(n-1,2);
16     long long S=Pow(W,t>>1)-1;
17     if(S<0) S+=Mod;
18     S=M(S,Pow(((W-1)<0)?(W-1+Mod):(W-1),Mod-2));
19     if(t&1){
20         S=(M(M(M(n-1,n-2),sum),S)+sum)%Mod;
21         for(int i=1;i<=n;++i) printf("%lld\n",(S-a[i]+Mod)%Mod);
22     }
23     else{
24         S=M(M(n-2,sum),S);
25         for(int i=1;i<=n;++i) printf("%lld\n",(S+a[i])%Mod);
26     }
27     return 0;
28 }

【T3】花费

题意:

在一个字符串中添加或删除字母,每个字母的添加与删除费用确定,问把字符串变成回文的,最少花费多少?

题解:

看做字符串与其逆序做匹配,简单的序列DP。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 int n,m,a[4001],b[4001],f[4001][4001],v[256];
 5 char str[4001];
 6 int main(){
 7     freopen("cost.in","r",stdin);
 8     freopen("cost.out","w",stdout);
 9     scanf("%d%d",&n,&m);
10     scanf("%s",str);
11     for(int i=1;i<=m;++i) b[m-i+1]=a[i]=str[i-1];
12     for(int i=1,x,y;i<=n;++i) scanf("%s%d%d",str,&x,&y), v[(int)str[0]]=std::min(x,y);
13     memset(f,0x3f,sizeof f);
14     f[0][0]=0;
15     for(int i=1;i<=m;++i) f[i][0]=f[i-1][0]+v[a[i]]/*, printf("%d,%d: %d\n",i,0,f[i][0])*/;
16     for(int j=1;j<=m;++j) f[0][j]=f[0][j-1]+v[b[j]]/*, printf("%d,%d: %d\n",0,j,f[0][j])*/;
17     for(int Q=1;Q<=m+m;++Q){
18         for(int i=std::max(1,Q-m);i<=m&&i<Q;++i){
19             int j=Q-i;
20             f[i][j]=std::min(f[i][j],f[i-1][j]+v[a[i]]);
21             f[i][j]=std::min(f[i][j],f[i][j-1]+v[b[j]]);
22             if(a[i]==b[j]) f[i][j]=std::min(f[i][j],f[i-1][j-1]);
23 //            printf("%d,%d: %d\n",i,j,f[i][j]);
24         }
25     }
26     printf("%d",f[m][m]/2);
27     return 0;
28 }

转载于:https://www.cnblogs.com/PinkRabbit/p/7756254.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值