第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(南京)
L Let's Play Curling

分析:
转换一下就是 找每两个b之间 最多有多少个a
先离散化 再树状数组维护一下就好
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=2e5+5;
int n,m,cnt,ans;
int a[maxn],b[maxn],t[maxn],tr[maxn];
void upd(int x){
while(x<=maxn)tr[x]++,x+=lowbit(x);
}
int query(int x){
int res=0;
while(x)res+=tr[x],x-=lowbit(x);
return res;
}
void solve();
int main(){
int T;
cin>>T;
while(T--)solve();
return 0;
}
void solve(){
scanf("%d%d",&n,&m);
ans=0;cnt=0;
memset(tr,0,sizeof(tr));
for(int i=1;i<=n;i++)scanf("%d",&a[i]),t[++cnt]=a[i];
for(int i=1;i<=m;i++)scanf("%d",&b[i]),t[++cnt]=b[i];
b[0]=t[++cnt]=0,b[m+1]=t[++cnt]=1e9+7;
sort(t+1,t+1+cnt);
int len=unique(t+1,t+1+cnt)-t-1;
for(int i=1;i<=n;i++){
int id=lower_bound(t+1,t+1+len,a[i])-t;
a[i]=id,upd(a[i]);
}
for(int i=0;i<=m+1;i++){
int id=lower_bound(t+1,t+1+len,b[i])-t;
b[i]=id;
}
sort(b,b+2+m);
for(int i=1;i<=m+1;i++)
ans=max(ans,query(b[i]-1)-query(b[i-1]));
if(!ans)cout<<"Impossible"<<endl;
else cout<<ans<<endl;
}
F.Fireworks

分析:

式子很好列出 关键在于我们担心计算的时候会掉精度 但是最后打出来貌似不会掉
注意点:因为是要求整数 但是三分只有在小数的时候才能准确找到峰值 所以三分整数最终需要判断+1和-1的值
同样也可以三分小数 最终在小数前后位置找
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
double n,m,p;
void solve();
double calc(int x){
return (x*n+m)/(1-pow((10000-p)/10000,x));
}
int main(){
int T;
cin>>T;
while(T--)solve();
return 0;
}
void solve(){
cin>>n>>m>>p;
int l=1,r=1e9,ans;
while(l<=r){
int mid=(l+r)/2;
int midd=(l+mid)/2;
if(calc(mid)-calc(midd)>=1e-8)ans=midd,r=mid-1;
else l=midd+1;
}
int res;
if(calc(ans)-calc(ans-1)>=1e-8)res=ans-1;
if(calc(ans)-calc(ans+1)>=1e-8)ans++;
if(calc(ans)-calc(res)>=1e-8)ans=res;
printf("%.6lf\n",calc(ans));
}
K Co-prime Permutation
签到题
E Evil Coordinate

分析:
很明显 只要障碍不在终点 或者只能朝x轴方向走(只能朝y轴方向走)并且一定会经过障碍点
其他情况一定是能构造出一个方案 使得路径不经过障碍点
分情况写起来很麻烦
题解给出了偷懒的智慧
完全用不着分类讨论
可以证明存在一种答案,使得相同的方向是连续排在一起的(按原字符串中有 1/2/3/4 种字母进行分类讨论)
枚举 UDLR 的 24 种排列即可
只要把所有情况都列出来 再判断即可
总结出经验 遇到分类讨论构造的题目 如果在数据范围允许的情况 可以遍历所有的构造方案
#include<bits/stdc++.h>
using namespace std;
int t,x,y,dx[]={0,0,-1,1},dy[]={1,-1,0,0},dd[200],c[4];
string s,ans,a="UDLR";
void solve()
{
memset(c,0,sizeof c);
scanf("%d%d",&x,&y);
cin>>s;
for(auto ch:s)c[dd[ch]]++;
int xx=c[3]-c[2],yy=c[0]-c[1];
if((xx==x&&yy==y)||(x==0&&y==0)){puts("Impossible");return;}
int q[4]={0,1,2,3};
do
{
xx=0,yy=0;
ans="";
for(int i=0;i<4;i++)
{
int p=q[i];
for(int j=0;j<c[p];j++)
{
ans+=a[p];
xx+=dx[p];
yy+=dy[p];
if(xx==x&&yy==y)goto End;
}
}
cout<<ans<<endl;
return;
End:;
}while(next_permutation(q,q+4));
puts("Impossible");
}
int main()
{
for(int i=0;i<4;i++)dd[a[i]]=i;
scanf("%d",&t);
while(t--)solve();
return 0;
}
M Monster Hunter

分析:
很好分析的一道树形dp问题
因为相连点会造成影响 所以设置dp数组的时候多开一维 表示该点选择与否
dp[u][k][1] 表示u子树选择k个点 并且u选择的最小值
dp[u][k][0]表示u子树选择k个点 并且u不选择的最小值
不选择的点默认只能用魔法消掉
注意点:一开始一直觉得dfs里面两重循环会爆掉 但是实际上这个题目不会
在剪枝的条件下 具体复杂度肯定比三方小很多 比平方要大些
上下限都需要剪枝 不剪枝会TLE!!!!!!
总结一下就是:在n方的复杂度下是能够进行树上背包的 但是必须上下限都得剪枝
#include<bits/stdc++.h>
using namespace std;
int tot,g[2001],sz[2001],a[2001];
long long dp[2001][2001][2];
struct edge{
int to,next;
}e[1000001];
void add(int a,int b)
{
tot++;
e[tot].to=b;
e[tot].next=g[a];
g[a]=tot;
}
void dfs(int x)
{
sz[x]=1;
dp[x][1][0]=a[x];
dp[x][0][1]=0;
for(int i=g[x];i;i=e[i].next)
{
dfs(e[i].to);
sz[x]+=sz[e[i].to];
for(int j=sz[x];j>=0;j--)
{
int di=max(0,j-sz[x]+sz[e[i].to]);//上下限都需要剪枝
for(int k=min(j,sz[e[i].to]);k>=di;k--)
{
dp[x][j][0]=min(dp[x][j][0],dp[x][j-k][0]+min(dp[e[i].to][k][0]+a[e[i].to],dp[e[i].to][k][1]));
dp[x][j][1]=min(dp[x][j][1],dp[x][j-k][1]+min(dp[e[i].to][k][0],dp[e[i].to][k][1]));
//cout<<j<<" "<<k<<endl;
}
}
}
}
signed main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=1e18;
for (int i=1;i<=n;i++)
g[i]=0;tot=0;
for(int i=2;i<=n;i++)
{
int x;scanf("%d",&x);
add(x,i);
}
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dfs(1);
for(int i=n;i>=0;i--)
{
printf("%lld ",min(dp[1][i][0],dp[1][i][1]));
}
puts("");
}
return 0;
}
H Harmonious Rectangle
给你一个n*m的矩阵,每个点都可以取三种颜色。问你有多少种染色方法可以使得矩阵中至少一对(x1,y1)与(x2,y2)满足下列式子。
(a[y1][x1] == a[y2][x1] && a[y1][x2] == a[y2][x2]) ||(a[y1][x1] == a[y1][x2] && a[y2][x1] == a[y2][x2]))
分析:
其实很好想到 当n m 足够大的时候 一定是存在幽默矩阵的
考虑在矩阵中选择任意两行a和b
a1 a2 a3 a4 a5 a6 ......
b1 b2 b3 b4 b5 b6 ......
可知(a1,b1)有3*3=9种组合方式。根据鸽笼原理,或者直接动脑子想想,可以想到当列数>9时,一定存在另一组(ai,bi)与(a1,b1)相同。同理,当行数>9时,一定存在两组相同的列。所以可以分类讨论:
if n=1 || m=1 ,必为0
else if n>9 || m>9,结果为pow(3,n*m)
else 在9*9范围内,可以DFS剪枝搜索,然后把结果打个表。
打表
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod_mul(ll a, ll b, ll n) {
a %= n; //优化后只要在开头模一次,其他的地方用if,效率提升近一倍
b %= n;
ll res = 0;
while (b) {
if (b & 1) {
res += a;
if (res > n) res -= n;
}
a += a;
if (a > n) a -= n;
b /= 2; //没开o2的话要换成b>>=1
}
return res;
}
ll mod_exp(ll a, ll b, ll n) {
ll res = 1;
a = a % n;
while (b) {
if (b & 1) res = mod_mul(res, a, n);
a = mod_mul(a, a, n);
b /= 2;
}
return res;
}
const ll MOD = 1e9 + 7;
const int MAXN = 12;
int a[MAXN][MAXN];
int n, m;
long long gcnt;
void get_next() {
const static int base = 3;
int carry = 1;
for (int i = n - 1; i >= 0; i--) {
for (int j = m - 1; j >= 0; j--) {
a[i][j] += carry;
carry = 0;
if (a[i][j] >= base) {
carry = a[i][j] / base;
a[i][j] %= base;
}
}
}
}
void print() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
printf("\n");
}
bool check() {
for (int y1 = 0; y1 < n; y1++) {
for (int x1 = 0; x1 < m; x1++) {
for (int y2 = y1 + 1; y2 < n; y2++) {
for (int x2 = x1 + 1; x2 < m; x2++) {
if ((a[y1][x1] == a[y2][x1] && a[y1][x2] == a[y2][x2]) ||
(a[y1][x1] == a[y1][x2] && a[y2][x1] == a[y2][x2]))
return true;
}
}
}
}
return false;
}
void dfs(int xx, int yy) {
for (int color = 0; color < 3; color++) {
int x2 = xx;
int y2 = yy;
a[y2][x2] = color;
bool flag = true;
for (int y1 = 0; y1 < y2 && flag; y1++) {
for (int x1 = 0; x1 < x2 && flag; x1++) {
if ((a[y1][x1] == a[y2][x1] && a[y1][x2] == a[y2][x2]) ||
(a[y1][x1] == a[y1][x2] && a[y2][x1] == a[y2][x2])) {
flag = false;
gcnt +=
mod_exp(3LL, (1LL * (n - 1 - y2) * (m) + (m - 1) - x2), MOD);
gcnt %= MOD;
}
}
}
if (flag) {
x2++;
if (x2 >= m) {
x2 = 0;
y2++;
if (y2 >= n){
continue;
}
}
dfs(x2, y2);
}
}
a[yy][xx]=-1;
}
ll solve_dfs(){
gcnt=0;
dfs(0,0);
return gcnt;
}
int solve_exhaust() {
int p = pow(3, n * m);
int cnt = 0;
for (int i = 0; i < p; i++) {
if (check()) {
cnt++;
// print();
}
// print();
get_next();
}
return cnt;
}
int main() {
for(n =1; n<=9;n++){
for(m=1;m<=9;m++){
if(m==1||n==1) printf("0, ");
else{
memset(a,0,sizeof(a));
printf("%lld, ",solve_dfs());
//printf("%lld, ",solve_exhaust());
}
}
printf("\n");
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9+7;
int n,m;
ll mod_mul(ll a, ll b, ll n) {
a %= n; //优化后只要在开头模一次,其他的地方用if,效率提升近一倍
b %= n;
ll res = 0;
while (b) {
if (b & 1) {
res += a;
if (res > n) res -= n;
}
a += a;
if (a > n) a -= n;
b /= 2; //没开o2的话要换成b>>=1
}
return res;
}
ll mod_exp(ll a, ll b, ll n) {
ll res = 1;
a = a % n;
while (b) {
if (b & 1) res = mod_mul(res, a, n);
a = mod_mul(a, a, n);
b /= 2;
}
return res;
}
ll dat[9][9]={0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 15, 339, 4761, 52929, 517761, 4767849, 43046721, 387420489,
0, 339, 16485, 518265, 14321907, 387406809, 460338013, 429534507, 597431612,
0, 4761, 518265, 43022385, 486780060, 429534507, 792294829, 175880701, 246336683,
0, 52929, 14321907, 486780060, 288599194, 130653412, 748778899, 953271190, 644897553,
0, 517761, 387406809, 429534507, 130653412, 246336683, 579440654, 412233812, 518446848,
0, 4767849, 460338013, 792294829, 748778899, 579440654, 236701429, 666021604, 589237756,
0, 43046721, 429534507, 175880701, 953271190, 412233812, 666021604, 767713261, 966670169,
0, 387420489, 597431612, 246336683, 644897553, 518446848, 589237756, 966670169, 968803245};
int main() {
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
if(n==1||m==1){
printf("0\n");
}
else if(n<=9||m<=9){
n--;
m--;
printf("%lld\n",dat[n][m]%MOD);
}
else{
printf("%lld\n",mod_exp(3,1LL*n*m,MOD));
}
}
return 0;
}

浙公网安备 33010602011771号