链接:https://ac.nowcoder.com/acm/contest/4853/A
一道挺普通的二进制题目,和洛谷之前做的谔运算有点像,可能还更简单一点。
#include
using namespace std;
typedef long long ll;
ll counter1[128]={0};
ll counter2[128]={0};//其实不用开这么大,32就够了。。下面循环次数也是。
int main()
{
ll n;
cin>>n;
for(ll i=0;i<n;i++){
ll tmp;
cin>>tmp;
for(ll j=0;tmp;j++){
counter1[j]+=(tmp&1);
tmp>>=1;
}
}
for(ll i=0;i<128;i++){
counter2[i]=counter1[i]*counter1[i];
}
for(ll i=0;i<127;i++){
counter2[i+1]+=counter2[i]/2;
counter2[i]=counter2[i]%2;
}
ll ans=0;
for(ll i=127;i>=0;i--){
ans<<=1;
ans+=counter2[i];
}
cout<<ans<<endl;
return 0;
}
思维题(弱项),任意选取两个点,则这两个点(记为A、B)和另外的(n-2)个点可以分别形成(n-2)个三角形,那么,周长和当中,将包括(n-2)个|AB|,依照这个原理可以算出来(太牛逼了orz)。
#include
#include
#include
using namespace std;
typedef long long ll;
const ll maxn=1e3+10;
const ll mod=998244353;
ll point[maxn][2];
ll cal(int i,int j){
return abs(point[i][0]-point[j][0])+abs(point[i][1]-point[j][1]);
}
int main()
{
ll n;
scanf("%lld",&n);
for(ll i=0;i<n;i++){
scanf("%lld %lld",&point[i][0],&point[i][1]);
}
ll ans=0;
for(ll i=0;i<n;i++){
for(ll j=i+1;j<n;j++){
ans=(ans%mod+(n-2)*cal(i,j)%mod)%mod;
}
}
printf("%lld\n",ans%mod);
return 0;
}
一开始往排列组合方向去想了。。发现想不通,可能还是抓不住排列组合的特点吧。
这道用dp做,需要二维数组。dp[i][j]表示在前i个字符中,组成长度为j的组合的个数,状态转移方程(不考虑重复)也比较容易地出来:dp[i][j] 为 dp[i-1][j](表示前面得出的组成长度为j的组合的数目)+dp[i-1][j-1](表示前面的已经组成了j-1长度的组合再添加上目前位置的字符所形成的组合的数目),那重复的怎么办呢?
先分析下:重复是从哪里来的呢?其实就是:假设目前该位上的字符是b,则,从dp[i-1][j-1]生成过来的代表的字符串结尾就是b,若前面也有哪一位(记为pre)上的字符是b,因为目前位置在它的后面,所以dp[i-1][j]就含有和现在要生成的字符串相互重复的(因为新生成的结尾也会是b,dp[i-1][j-1]也会含有和dp[pre-1][j-1]相同的组合,则可能会生成重复的组合)。因此,需要一个pre数组,以字母为键,记录该字符出现的上一个位置,方便查重。
#include
using namespace std;
const int maxn=1e4+10;
typedef long long ll;
const ll mod=1e9+7;
char s[maxn];
ll dp[maxn][maxn];
int pre[maxn];
ll k,n;
void pr(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}
cout<<endl<<endl;
}
int main()
{
scanf("%lld %lld",&n,&k);
scanf("%s",s+1);
dp[0][0]=1;
for(int i=1;i<=n;i++){
dp[i][0]=1;
for(int j=1;j<=i;j++){
dp[i][j]=(dp[i-1][j-1]+dp[i-1][j]+mod)%mod;
if(pre[s[i]-'a']){
dp[i][j]-=dp[pre[s[i]-'a']-1][j-1];
}
}
// pr();
pre[s[i]-'a']=i;
}
printf("%lld",(dp[n][k]+mod)%mod);
return 0;
}
就是考的扩展欧几里得算法,需要注意的就是如果算出来x是负的怎么办的处理,不多说,多复习就行。
#include
using namespace std;
typedef long long ll;
ll a,b,c,k;
void extend_gcd(ll a,ll b,ll&x,ll&y)
{
if(b==0)
{
x=1;
y=0;
return;
}
extend_gcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-(a/b)*y;
}
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main()
{
scanf("%lld%lld%lld%lld",&a,&b,&c,&k);
for(int i=0;i<=k/c;i++){
ll cc=k-i*c;
ll x,y;
extend_gcd(a,b,x,y);
ll d=gcd(a,b);
if(cc%d!=0) continue;
x=cc/d*x,y=cc/d*y;
x=(x%(b/d)+(b/d))%(b/d);
y=(cc-x*a)/b;
if(x>=0&&y>=0){
printf("%lld %lld %lld",x,y,i);
return 0;
}
}
return 0;
}
ps.有个奇葩的事,暴力竟然能过?
#include
#include
using namespace std;
typedef long long ll;
ll a,b,c,k;
int main()
{
scanf("%lld %lld %lld %lld",&a,&b,&c,&k);
if(k%a==0){
printf("%lld %lld %lld",k/a,0,0);
}
else if(k%b==0){
printf("%lld %lld %lld",0,k/b,0);
}
else if(k%c==0){
printf("%lld %lld %lld",0,0,k/c);
}
else{
for(ll i=k/a;i>=0;i--){
ll left1=k-i*a;
for(ll j=left1/b;j>=0;j--){
ll left2=left1-j*b;
if(left2%c==0){
printf("%lld %lld %lld",i,j,(left2/c));
return 0;
}
}
}
}
return 0;
}